在Go里面,channel是一种非常关键、方便的通信机制,但是通常我们想要将消息传递给多个消费者可能需要建立多个通道。只要channel的某个消息被某个消费者读取了,那么该值就会被移除,而其他消费者将不会再消费这个消息。如果我们想实现广播方式我们可能需要将消息写入N个channel。例如:
func main() { ch := make(chan int) var wg sync.WaitGroup wg.Add(2) go func() { for v := range ch { fmt.Println("A", v) } wg.Done() }() go func() { for v := range ch { fmt.Println("B", v) } wg.Done() }() ch <- 1 ch <- 2 close(ch) wg.Wait() }
运行结果:
B 1 A 2
或者是:
B 1 B 2
或者是:
A 2 B 1
或者是:
A 1 A 2
从上面可以看出输出结果不定,每个goroutine消费者只能消费到一条消息。如果我们希望所有消费者都能收到每个消息,我们需要更多的频道。例如:
package main import ( "fmt" "sync" ) func main() { chA := make(chan int) chB := make(chan int) var wg sync.WaitGroup wg.Add(2) go func() { for v := range chA { fmt.Println("A", v) } wg.Done() }() go func() { for v := range chB { fmt.Println("B", v) } wg.Done() }() for i := 0; i < 10; i++ { chA <- i chB <- i } close(chA) close(chB) wg.Wait() }
现在每个消费者接收到了每一个消息。我们对此并不满意。现在我们必须跟要维护多个channel,如果我们想要动态添加或删除消费者怎么办?让我们看看第一个中间改进。
package main import ( "fmt" "sync" ) func main() { chA := make(chan int) chB := make(chan int) chBroadcast := make(chan int) var wg sync.WaitGroup wg.Add(3) go func() { for v := range chBroadcast { chA <- v chB <- v } close(chA) close(chB) wg.Done() }() go func() { for v := range chA { fmt.Println("A", v) } wg.Done() }() go func() { for v := range chB { fmt.Println("B", v) } wg.Done() }() for i := 0; i < 10; i++ { chBroadcast <- i } close(chBroadcast) wg.Wait() }
请注意区别?现在我们只需要将我们的值写入一个channel chBroadcast。这是我们的广播channel。但现在让动态添加和删除消费者变得更复杂.下面进行了一次封装,封装之后消费者的添加和删除就变得比较容易了。
type BroadcastService struct { // 这是消费者监听的通道 chBroadcast chan int // 转发给这些channel chListeners []chan int // 添加消费者 chNewRequests chan (chan int) // 移除消费者 chRemoveRequests chan (chan int) } // 创建一个广播服务 func NewBroadcastService() *BroadcastService { return &BroadcastService{ chBroadcast: make(chan int), chListeners: make([]chan int, 3), chNewRequests: make(chan (chan int)), chRemoveRequests: make(chan (chan int)), } } // 这会创建一个新消费者并返回一个监听通道 func (bs *BroadcastService) Listener() chan int { ch := make(chan int) bs.chNewRequests <- ch return ch } // 移除一个消费者 func (bs *BroadcastService) RemoveListener(ch chan int) { bs.chRemoveRequests <- ch } func (bs *BroadcastService) addListener(ch chan int) { for i, v := range bs.chListeners { if v == nil { bs.chListeners[i] = ch return } } bs.chListeners = append(bs.chListeners, ch) } func (bs *BroadcastService) removeListener(ch chan int) { for i, v := range bs.chListeners { if v == ch { bs.chListeners[i] = nil // 一定要关闭! 否则监听它的groutine将会一直block close(ch) return } } } func (bs *BroadcastService) Run() chan int { go func() { for { // 处理新建消费者或者移除消费者 select { case newCh := <-bs.chNewRequests: bs.addListener(newCh) case removeCh := <-bs.chRemoveRequests: bs.removeListener(removeCh) case v, ok := <-bs.chBroadcast: // 如果广播通道关闭,则关闭掉所有的消费者通道 if !ok { goto terminate } // 将值转发到所有的消费者channel for _, dstCh := range bs.chListeners { if dstCh == nil { continue } dstCh <- v } } } terminate: //关闭所有的消费通道 for _, dstCh := range bs.chListeners { if dstCh == nil { continue } close(dstCh) } }() return bs.chBroadcast } func main() { bs := NewBroadcastService() chBroadcast := bs.Run() chA := bs.Listener() chB := bs.Listener() var wg sync.WaitGroup wg.Add(2) go func() { for v := range chA { fmt.Println("A", v) } wg.Done() }() go func() { for v := range chB { fmt.Println("B", v) } wg.Done() }() for i := 0; i < 3; i++ { chBroadcast <- i } bs.RemoveListener(chA) for i := 3; i < 6; i++ { chBroadcast <- i } close(chBroadcast) wg.Wait() }