• golang RWMutex RLock重入导致死锁


    现象

    一个组件实现了raft分布式协议,在分布式部署环境中来进行选主,在某客户现场突然发生文件句柄泄露,在打印某些错误日志后,几个小时内没有日志打印,然后某个协程突然报无可用的文件句柄。

    分析

    经过代码和日志分析,组件正常每分钟会打印所有部署节点的日志信息,没有打印日志说明定时器处理逻辑for...select里面某个函数逻辑卡住了,然后发生文件句柄泄露,经过梳理是在响应心跳的逻辑没有回,导致一直创建协程。心跳响应逻辑和定时器处理逻辑中有用到同一个锁,初步判断为这个锁发生死锁。

    在本地环境复现了后,通过debug/pprof分析,确实有四处在等待该锁,两处等待写锁,两处等待读锁,但是代码看起来都很正常;pprof分析也没有提示死锁。然后通过搜索引擎搜索关键词“RWMutex 死锁”,找到一篇文件说RWMutex RLock重入可能导致死锁,如果网络异常,有分布式节点疑似下线时,代码中确实有一处会有该锁的RLock同一协程两次重入调用。

    RLock重入死锁复现

     1 func TestDeadLock(t *testing.T) {
     2     var l sync.RWMutex
     3     var wg sync.WaitGroup
     4     wg.Add(2)
     5 
     6     c := make(chan int)
     7     go func() {
     8         defer wg.Done()
     9 
    10         l.RLock()
    11         defer l.RUnlock()
    12         t.Log("acquire RLock first")
    13 
    14         c <- 1
    15         runtime.Gosched()
    16 
    17         t.Log("wait readLock")
    18         l.RLock()
    19         defer l.RUnlock()
    20         t.Log("acquire RLock second")
    21     }()
    22 
    23     go func() {
    24         defer wg.Done()
    25 
    26         <-c
    27 
    28         t.Log("wait writeLock")
    29         l.Lock()
    30         defer l.Unlock()
    31         t.Log("acquire Lock")
    32     }()
    33 
    34     wg.Wait()
    35     t.Log("test finish")
    36 }

    通过以上测试代码,很容易复现该死锁现象,而在java中可重入读写锁读锁重入不会导致死锁,所以刚开始看到RLock重入时也没有想到该问题。

    源码分析

    参考文档

    golang RWMutex RLock重入导致死锁

  • 相关阅读:
    PHP无限极分类生成树方法
    如何用c#本地代码实现与Webbrowser中的JavaScript交互
    在sqlite中,如何删除字段? how to drop a column in sqlite
    如何在centos下配置redis开机自启动
    WinForm中的图表控件Chart
    XPath高级用法(冰山一角)
    windows下MySQL的安装(非安装包)
    WinFrom下Webbrowser加载自定义页面的技巧
    .net下使用最小堆实现TopN算法
    .net程序中http请求的超时配置
  • 原文地址:https://www.cnblogs.com/luoming1224/p/14636543.html
Copyright © 2020-2023  润新知