Я не могу найти способ иметь простую (когда-либо) l oop упаковку выбора, с двумя случаями (один получает из канала результатов, а другой - как случай <), который вернется из функции). Будет ли это лучше? </p>
Если канал закрыт после завершения работы всех авторов, вы можете использовать простой for ... range
l oop:
for result := range ch {
... do something with the result ...
}
In Чтобы этот простой вариант работал, канал должен стать закрытым, в противном случае for
l oop не прекратится.
Должен ли я где-то закрыть канал после того, как все итерации?
Если это вообще возможно, да.
я действительно не пробовал группы ожидания ...
A sync.WaitGroup
или что-то очень похожее, это почти наверняка путь к go здесь. Каждая процедура, которая может записать в канал, должна учитываться изначально, например:
var wg Sync.WaitGroup
wg.Add(ITERATIONS)
Затем вы можете просто создать все свои программы, которые пишут, и позволить им работать. При каждом запуске он вызывает wg.Done()
, чтобы указать, что это закончено.
Вы тогда - где-нибудь; часть , где немного сложна - позвоните wg.Wait()
, чтобы дождаться завершения работы всех авторов. Когда все авторы указывают, что они сделали, вы можете close()
канал.
Обратите внимание, что если вы вызываете wg.Wait()
из той же самой программы, которая читает канал, т.е. процедура, которая будет запускать цикл for result := range ...
- у вас проблема: вы не можете одновременно читать с канала и , ожидая, когда записывающие устройства запишут на канал. Так что вам нужно либо позвонить wg.Wait()
после того, как l oop закончится, что слишком поздно; или перед запуском l oop, что слишком рано.
Это проясняет проблему и ее решение: вы должны прочитать из канала в одной процедуре и выполнить ожидание, а затем закрытие в другая программа Самое большее, одна из этих программ может быть основной, которая изначально вошла в функцию main
.
Обычно довольно просто сделать процедуру ожидания и закрытия закрытой:
go func() {
wg.Wait()
close(results)
}()
например.
что, если по какой-то причине один из горутинов завершится неудачей?
Вам нужно будет точно определить, что вы имеете в виду на терпит неудачу здесь, но если вы имеете в виду: что, если сама вызываемая программа вызывает, скажем, panic
и, следовательно, не получает на свой вызов wg.Done()
, вы можете использовать defer
, чтобы убедиться, что wg.Done()
происходит даже в пани c:
func(args) {
defer wg.Done()
... do the work ...
}
wg.Add(1)
go func(args) // `func` will definitely call `wg.Done`, even if `func` panics
Должен ли я передавать каналы в забавных c аргументах или делать их глобальными для программы как она есть?
Стилистически глобальные переменные всегда немного беспорядочные. Это не означает, что вы не можете использовать их; Вам решать, просто помните все компромиссы. Переменные замыкания не такие беспорядочные, но не забывайте соблюдать осторожность с for
l oop переменными итерации:
for i := 0; i < 10; i++ {
go func() {
time.Sleep(50 * time.Millisecond)
fmt.Println(i) // BEWARE BUG: this prints 10, not 0-9
}()
}
ведет себя плохо. Попробуйте это на Go детской площадке ; обратите внимание, что go vet
теперь жалуется на неправильное использование i
здесь.
Я перенес ваш оригинальный пример кода на игровую площадку Go и внес в него минимальные изменения, как описано выше. Результат здесь . (Чтобы сделать его менее медленным, я заставил сны ждать n-сто миллисекунд вместо n секунд.)