使用 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)
}
}
通过一些实验,我发现我可以像下面这样天真地引入运行时错误:
如果我将 i 减为 1,第一条消息收到,但第二条消息悄无声息地丢失(没有迹象表明我无意中忽略了它)。
如果我将 i 增加到 3,两条消息都会收到,但我收到 fatal error: all goroutines are asleep - deadlock!
Reading ahead and searching for that error message on Whosebug 我可以看到 WaitGroups 解决了这些类型的问题。但是它们似乎不适用于select
,所以我觉得我一定是遗漏了什么。
是否有一种语言结构(如 if/then/else
)或软件模式可用于防止或减轻实际代码中的这些错误?
从概念上讲,您可以通过正确设计软件来缓解这种情况。如果您有两个频道,并且每个频道最多收到一条消息,请不要尝试从中读取 3 次。这与尝试将三个项目放入一个双元素数组或尝试除以除数为 0 的两个数字没有什么不同。在所有这些情况下,语言都提供了发现错误和从错误中恢复的方法,但如果您实际上正在生产这些错误,表示存在逻辑或设计缺陷。
您需要确保您的通道具有均衡数量的读取和写入,并且发送端在没有其他要发送的消息时关闭通道,以便接收方可以停止等待不会到来的消息。否则你最终会遇到等待的问题,或者缓冲区中的消息被忽略。
在这种非常特殊的情况下,如果您想从两个通道读取但仅当消息准备就绪时,您可以添加一个 default
案例,如果没有通道准备好读取,将调用该案例,但是这是针对您的频道尚未但最终会变得准备就绪的情况。提供 default
并不是一个很好的解决方案来覆盖通道 永远不会 准备就绪但您仍在尝试从中读取的错误;这表明 logic-level 缺陷需要修复。
我是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)
}
}
通过一些实验,我发现我可以像下面这样天真地引入运行时错误:
如果我将 i 减为 1,第一条消息收到,但第二条消息悄无声息地丢失(没有迹象表明我无意中忽略了它)。
如果我将 i 增加到 3,两条消息都会收到,但我收到
fatal error: all goroutines are asleep - deadlock!
Reading ahead and searching for that error message on Whosebug 我可以看到 WaitGroups 解决了这些类型的问题。但是它们似乎不适用于select
,所以我觉得我一定是遗漏了什么。
是否有一种语言结构(如 if/then/else
)或软件模式可用于防止或减轻实际代码中的这些错误?
从概念上讲,您可以通过正确设计软件来缓解这种情况。如果您有两个频道,并且每个频道最多收到一条消息,请不要尝试从中读取 3 次。这与尝试将三个项目放入一个双元素数组或尝试除以除数为 0 的两个数字没有什么不同。在所有这些情况下,语言都提供了发现错误和从错误中恢复的方法,但如果您实际上正在生产这些错误,表示存在逻辑或设计缺陷。
您需要确保您的通道具有均衡数量的读取和写入,并且发送端在没有其他要发送的消息时关闭通道,以便接收方可以停止等待不会到来的消息。否则你最终会遇到等待的问题,或者缓冲区中的消息被忽略。
在这种非常特殊的情况下,如果您想从两个通道读取但仅当消息准备就绪时,您可以添加一个 default
案例,如果没有通道准备好读取,将调用该案例,但是这是针对您的频道尚未但最终会变得准备就绪的情况。提供 default
并不是一个很好的解决方案来覆盖通道 永远不会 准备就绪但您仍在尝试从中读取的错误;这表明 logic-level 缺陷需要修复。