чтобы запустить бесконечный цикл выполнения двух горутин, я могу использовать приведенный ниже код:
после получения сообщения он запустит новую горутину и будет работать вечно.
c1 := make(chan string)
c2 := make(chan string)
go DoStuff(c1, 5)
go DoStuff(c2, 2)
for ; true; {
select {
case msg1 := <-c1:
fmt.Println("received ", msg1)
go DoStuff(c1, 1)
case msg2 := <-c2:
fmt.Println("received ", msg2)
go DoStuff(c2, 9)
}
}
Теперь я хотел бы иметь такое же поведение для N горутин, но как в этом случае будет выглядеть оператор select?
Это бит кода, с которого я начал, но я не понимаю, как кодировать оператор select
numChans := 2
//I keep the channels in this slice, and want to "loop" over them in the select statemnt
var chans = [] chan string{}
for i:=0;i<numChans;i++{
tmp := make(chan string);
chans = append(chans, tmp);
go DoStuff(tmp, i + 1)
//How shall the select statment be coded for this case?
for ; true; {
select {
case msg1 := <-c1:
fmt.Println("received ", msg1)
go DoStuff(c1, 1)
case msg2 := <-c2:
fmt.Println("received ", msg2)
go DoStuff(c2, 9)
}
}
Ответы:
Вы можете сделать это с помощью
Select
функции из пакета отражения :Вы передаете массив
SelectCase
структур, которые идентифицируют канал для выбора, направление операции и значение для отправки в случае операции отправки.Итак, вы можете сделать что-то вроде этого:
Вы можете поэкспериментировать с более конкретным примером здесь: http://play.golang.org/p/8zwvSk4kjx
источник
Этого можно добиться, заключив каждый канал в горутину, которая «пересылает» сообщения на общий «агрегированный» канал. Например:
Если вам нужно знать, из какого канала было отправлено сообщение, вы можете обернуть его в структуру с любой дополнительной информацией, прежде чем пересылать в агрегированный канал.
В моем (ограниченном) тестировании этот метод значительно уступает по производительности при использовании пакета отражения:
Код теста здесь
источник
b.N
в тесте. В противном случае результаты (которые делятся наb.N
, 1 и 2000000000 в вашем выводе) будут совершенно бессмысленными.reflect.Select
подходом) является то, что горутины делают буфер слияния как минимум одним значением на каждом сливаемом канале. Обычно это не будет проблемой, но в некоторых конкретных приложениях это может стать преградой :(.Чтобы расширить некоторые комментарии к предыдущим ответам и обеспечить более четкое сравнение, вот пример обоих подходов, представленных до сих пор с одним и тем же входом, срез каналов для чтения и функции для вызова для каждого значения, которое также должно знать, какое канал, из которого получено значение.
Есть три основных различия между подходами:
Сложность. Хотя отчасти это может быть предпочтением читателя, я считаю, что канальный подход более идиоматичен, прямолинейен и удобочитаем.
Производительность. В моей системе Xeon amd64 goroutines + channels out выполняет решение для отражения примерно на два порядка (в целом отражение в Go часто медленнее и должно использоваться только в случае крайней необходимости). Конечно, если есть какая-либо значительная задержка либо в функции, обрабатывающей результаты, либо в записи значений во входные каналы, эта разница в производительности может легко стать незначительной.
Семантика блокировки / буферизации. Важность этого зависит от варианта использования. Чаще всего это либо не имеет значения, либо небольшая дополнительная буферизация в решении для слияния горутин может быть полезна для повышения пропускной способности. Однако, если желательно иметь семантику, согласно которой разблокируется только один модуль записи, а его значение полностью обрабатывается до того, как любой другой модуль записи будет разблокирован, то этого можно достичь только с помощью решения отражения.
Обратите внимание, что оба подхода можно упростить, если либо «id» отправляющего канала не требуется, либо если исходные каналы никогда не будут закрыты.
Канал слияния горутин:
Выбор отражения:
[Полный код на игровой площадке Go .]
источник
select
илиreflect.Select
делает. Горутины будут продолжать вращаться, пока не поглотят все из каналов, поэтому нет четкого способаProcess1
выйти из игры раньше времени. Также есть вероятность проблем, если у вас несколько читателей, поскольку горутины буферизуют один элемент из каждого из каналов, чего не произойдет сselect
.select
вfor
цикле вместо более простогоfor range
цикла, который сейчас используется. Process2 потребуется вставить другой регистрcases
и специальный обработчик этого значенияi
.Почему этот подход не работает, если кто-то отправляет события?
источник
select
нескольких каналах (безdefault
предложения) заключается в том, что он эффективно ждет, пока хотя бы один будет готов, без вращения.Возможно более простой вариант:
Вместо того чтобы иметь массив каналов, почему бы не передать только один канал в качестве параметра функциям, выполняемым в отдельных горутинах, а затем прослушать канал в горутине потребителя?
Это позволяет вам выбирать только один канал в вашем слушателе, делая простой выбор и избегая создания новых горутин для агрегирования сообщений из нескольких каналов?
источник