使用 select 时如何避免死锁和静默错误?

How do I avoid deadlocks and silent errors when using select?

我是learning Go by example。我刚刚实现了一个 select 来等待多个频道,如下所示:

for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }

通过一些实验,我发现我可以像下面这样天真地引入运行时错误:

Reading ahead and searching for that error message on Whosebug 我可以看到 WaitGroups 解决了这些类型的问题。但是它们似乎不适用于select,所以我觉得我一定是遗漏了什么。

是否有一种语言结构(如 if/then/else)或软件模式可用于防止或减轻实际代码中的这些错误?

从概念上讲,您可以通过正确设计软件来缓解这种情况。如果您有两个频道,并且每个频道最多收到一条消息,请不要尝试从中读取 3 次。这与尝试将三个项目放入一个双元素数组或尝试除以除数为 0 的两个数字没有什么不同。在所有这些情况下,语言都提供了发现错误和从错误中恢复的方法,但如果您实际上正在生产这些错误,表示存在逻辑或设计缺陷。

您需要确保您的通道具有均衡数量的读取和写入,并且发送端在没有其他要发送的消息时关闭通道,以便接收方可以停止等待不会到来的消息。否则你最终会遇到等待的问题,或者缓冲区中的消息被忽略。

在这种非常特殊的情况下,如果您想从两个通道读取但仅当消息准备就绪时,您可以添加一个 default 案例,如果没有通道准备好读取,将调用该案例,但是这是针对您的频道尚未但最终会变得准备就绪的情况。提供 default 并不是一个很好的解决方案来覆盖通道 永远不会 准备就绪但您仍在尝试从中读取的错误;这表明 logic-level 缺陷需要修复。