[BUG]死锁2

单协程死锁场景:当网络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) // 永远不会执行到这里
}

死锁产生的原因分析:

  1. 主协程调用client.Call()获取锁后,等待server.Handle()返回
  2. server.Handle()又需要调用client.Call(),但此时锁已被主协程持有
  3. 形成主协程等server → server等client → client等主协程释放锁的循环等待

解决方案

  1. 将网络IO和业务处理分离到不同协程
  2. 异步回调

方案2极简代码示例:

func AsyncCall(request Message,cbk func(response Message))  {
  go func (){
       send(request)
       response<-messageChannel
       cbk(response)
    }
 }
滚动至顶部