В одном проекте мне необходимо было получать из одного источника данные и, в зависимости от текущего состояния, отдавать эти данные одному из множества ресиверов. Схему можно описать так: если некуда отдавать данные, то они должны просто обнуляться (либо - для любителей статистики - инкрементировать соответствующий счетчик); в случае готовности одного из ресиверов принимать данные - отдавать их ему. По условиям задачи ресивер готовый принимать данные всегда один.
В языке go есть такая конструкция:
for {
select {
case <-ch1:
doThing1()
case <-ch2:
doThing2()
}
}позволяющая читать с рандомного канала в котором есть данные.
Если ее немного изменить, то вполне подходит для решения:
package main
import (
"context"
"fmt"
"time"
)
var (
ii, ia, ib, ic int64
ctx context.Context
cancel context.CancelFunc
a_a = make(chan bool)
b_b = make(chan bool)
c_c = make(chan bool)
)
func gen() {
for i := 0; i < 1000000000; i++ {
select {
case a_a <- true:
ia++
case b_b <- true:
ib++
case c_c <- true:
ic++
}
ii++
}
}
func recv(ctx context.Context, ch chan bool, i int64) {
for {
select {
case <-ctx.Done():
fmt.Println(i)
return
case <-ch:
i++
}
}
}
func swap(i int64) {
switch i {
case 1:
if ctx != nil {
cancel()
}
ctx, cancel = context.WithCancel(context.Background())
go recv(ctx, a_a, ia)
case 2:
if ctx != nil {
cancel()
}
ctx, cancel = context.WithCancel(context.Background())
go recv(ctx, b_b, ib)
case 3:
if ctx != nil {
cancel()
}
ctx, cancel = context.WithCancel(context.Background())
go recv(ctx, c_c, ic)
case 4:
if ctx != nil {
cancel()
}
}
}
func main() {
swap(1)
time.Sleep(time.Second)
swap(2)
time.Sleep(time.Second)
swap(3)
time.Sleep(time.Second)
swap(4)
time.Sleep(time.Second)
fmt.Println(ii - (ia+ib+ic))
}
func init() {
go gen()
}
/* вывод
2704715
2730004
2741948
0
*/