Go开发人员在使用channel时常犯的一个错误是,对select在多个channel中的行为方式做出错误的假设。错误的假设可能会导致难以识别和重现的细微错误。假设我们要实现一个需要从两个channel接收消息的goroutine:
我们可能会决定像下面这样处理优先级:
for {select {case v := <-messageCh:fmt.Println(v)case <-disconnectCh:fmt.Println("disconnection, return")return}
}
我们使用select从多个channel接收消息。因为我们想优先考虑messageCh,所以可以假设应该首先编写messageCh情况,然后再编写disconnectCh情况。但是这段代码真的有用吗?让我们通过编写一个发送10条消息然后发送断开连接通知的虚拟生产者goroutine来尝试一下:
for i := 0; i < 10; i++ {messageCh <- i
}
disconnectCh <- struct{}{}
运行这个例子,如果messageCh被缓冲,下面是一个可能的输出:
0
1
2
3
4
disconnection, return
我们只收到了其中的5条。这是什么原因呢?它依赖于具有多个channel的select语句的规范: