我正在Go中进行初次体验,到目前为止,我真的很喜欢goroutine和channel结构。我想知道是否有一种惯用的方法来避免多个goroutine之间的双向通信中的死锁。考虑以下示例。共有三个goroutine:<生产者,工人和<生产者>控制器。
生产者产生整数。实际上这可能是数据的到来 例如通过网络连接。
工作者从生产者那里接收数据,并对其进行一些操作 它。然后,工作人员将修改后的数据定向到控制器。
在某些情况下,控制器会向工作人员发送命令。在里面 例如,如果接收到的整数大于180,则会发生这种情况。
当控制器尝试向工作程序发送命令,而工作程序尝试向控制器发送整数时,会发生死锁。
producerToWorker := make(chan int)
workerToController := make(chan int)
controllerToWorker := make(chan bool) // bool represents a command for this example
// Worker
go func() {
for {
select {
case i := <-producerToWorker:
// Do some processing and send to controller
workerToController <- (2 * i) + 1
case <-controllerToWorker:
// Would react to the command here
}
}
}()
// Controller
go func() {
for {
select {
case i := <-workerToController:
fmt.Println(i)
if i > 180 {
// Send a command to the worker
controllerToWorker <- true
}
}
}
}()
// Producer
for {
producerToWorker <- rand.Intn(100)
}
示例输出:
163
175
95
113
1
189 // No deadlock
23
125
179
57
149
23
91
191 // No deadlock
133
95
175
177
181 // No deadlock
17
175
63
27
181 // Deadlock!
fatal error: all goroutines are asleep - deadlock!
缓冲通道会使此死锁更不可能发生,但不能从逻辑上解决。如果可能,我想避免互斥。您如何在Go中处理此类情况?
编辑:给出更真实的描述:当我尝试实现Websocket客户端时遇到了这个问题。 WebSocket客户端( worker )连接到外部服务( producer ),并从外部服务( producerToWorker )接收消息,并将它们传递给控制器( workerToController )来处理收到的消息。控制器需要对收到的消息做出反应,例如,当收到无效消息( controllerToWorker )时发送响应或断开客户端连接。
答案 0 :(得分:1)
[有没有一种惯用的方法来避免多个goroutine之间的双向通信中的死锁?
不。没有“惯用的”或基于“模式”的。
您如何在Go中处理此类情况?
您重新设计。最好避免并行循环数据流。