
单协程死锁场景:当网络IO和业务处理在同一个协程中执行时,如果业务处理中进行同步RPC调用(会阻塞等待响应),而RPC响应又需要当前协程继续处理才能返回,就会形成循环等待的死锁。
以下是简单模拟:
package main
import (
"fmt"
"sync"
)
// 模拟RPC客户端
type RPCClient struct {
mu sync.Mutex
server *RPCServer
}
func (c *RPCClient) Call() int {
c.mu.Lock()
defer c.mu.Unlock()
// 模拟同步RPC调用(会阻塞等待服务端响应)
return c.server.Handle()
}
// 模拟RPC服务端
type RPCServer struct {
client *RPCClient
}
func (s *RPCServer) Handle() int {
// 服务端处理时需要再次调用客户端(形成循环依赖)
return s.client.Call() + 1
}
func main() {
client := &RPCClient{}
server := &RPCServer{client: client}
client.server = server // 互相引用
// 业务处理中发起RPC调用
result := client.Call()
fmt.Println(result) // 永远不会执行到这里
}
死锁产生的原因分析:
- 主协程调用
client.Call()
获取锁后,等待server.Handle()
返回 server.Handle()
又需要调用client.Call()
,但此时锁已被主协程持有- 形成
主协程等server → server等client → client等主协程释放锁
的循环等待
解决方案
- 将网络IO和业务处理分离到不同协程
- 异步回调
方案2极简代码示例:
func AsyncCall(request Message,cbk func(response Message)) {
go func (){
send(request)
response<-messageChannel
cbk(response)
}
}