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,就可以成功登录了。因为同一线程重复获取同一递归锁,不会发生死锁。

你可能感兴趣的:(mac开发系列32:NSLock死锁场景)