mac开发系列32:NSLock死锁场景

今天有用户反馈,mac微信卡死在登录界面,彩球一直转。从现象来看,是主线程卡住了,难道是死循环了?进一步地,开启调试符号("Build Settings"->"Strip Linked Product"置为NO),发现console里面狂刷主线程卡顿监控日志,主要堆栈如下:

      [I][][MMLagMonitor.mm, startMonitorWithCallback, 179]][INFO: Backtrace of Thread 775:
      libsystem_kernel.dylib 0x7fff9a50ac22 __psynch_mutexwait + 10
      Foundation 0x7fff866710e2 -[NSLock lock] + 145
      WeChat 0x108e7bc68 -[MMCGINotifyCenter addDelegateInternal:sessionId:] + 59
      WeChat 0x108e7bbef +[MMCGINotifyCenter addDelegate:sessionId:] + 83
      WeChat 0x108a03630 -[MMCGI registerDelegate:] + 37
      WeChat 0x108a034d8 -[MMCGI initWithItem:delegate:] + 140
      WeChat 0x10889a16d -[MMCGIService RequestCGI:delegate:] + 302
      WeChat 0x108e29126 -[AuthCGI QRCodeAuth:withPassword:] + 3859
      WeChat 0x108e9c1d0 -[AccountService QRCodeLoginWithUserName:password:] + 58
      WeChat 0x108dc5039 __34-[QRCodeLoginLogic setupQRCodeCGI]_block_invoke.38 + 330
      WeChat 0x108b3f1e3 -[QRCodeLoginCG<…>

可以看到,NSLock一直在锁等待,即死锁了。 那么死锁是怎么发生的呢?我们知道,NSLock是非递归锁,当同一线程重复获取同一非递归锁时,就会发生死锁。如下所示:

      NSLock *m_lock;
      [m_lock lock]; // 成功上锁
      do something....
      [m_lock lock]; // 上面已经上锁,这里阻塞等待锁释放,不会再执行下面,锁永远得不到释放,即死锁
      do something....
      [m_lock unlock]; // 不会执行到
      do something....
      [m_lock unlock];

   于是,断点跟踪堆栈中相关问题函数的调用关系,企图找到如上所示的嵌套上锁代码,结果如下:


简化如下:

        - (void)erase { 
        [m_lock lock]; 
        dealloc(); 
        [m_lock unlock];
        }
        - (void)dealloc { 
            erase();
        }

另一方面,用NSRecursiveLock或者@synchronized替代NSLock,就可以成功登录了。因为同一线程重复获取同一递归锁,不会发生死锁。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容