recursive_lock
问题
今天遇见了一个多线程死锁的问题。简化代码后大概是这样
1 | @interface ViewController (){ |
运行之后,日志卡在这里
1 | 2023-07-06 17:18:50.897834+0800 AAAA[82542:2621413] lock 1 |
点击 debug 调试后,发现 死锁了
一开始以为,这个锁是 递归锁
, 递归所就是可以重复的加锁,不应该有什么问题。后来问了 chatGPT,也说不会有问题,但它给出了一个重要的信息,就是重复加锁,应该是在同一个线程中
而且 Apple 文档也支出,针对的是同一个线程
梳理下调用流程,是这样的
首先我们在 global_queue
中执行 test
方法,其会在子线程,这时子线程 正常加锁,然后里边执行 dispatch_sync
。由于是 dispatch_sync
函数,后边的 解锁代码需要等待 这个函数内的 block 执行完毕。
但是在之前,我们通过 dispatch_async
在 main_queue
中执行了 test2
。
我们知道 dispatch_async
执行其实是把这个 test2 这个任务放在队尾。也就是说 NSLog(@"dispachsync");
需要等待 test2
执行完才可以执行。
我们来看 test2 函数。这个函数一开始进来先打印,这个没问题,接下来就开始加锁。虽然这个是递归锁,但是由于这是在不同的线程中,它其实就是一个 互斥锁
。但这个锁 在之前执行 test 的时候,已经被加锁了。这里只能等 test 中解锁。这样就死锁了
但这个也不是必现的。如果 main_queue
中 NSLog(@"dispachsync");
在 [self test2]
之前的话,则可以正常执行。
进一步思考,既然 递归锁
是为了避免在同一个线程中重复加锁导致的死锁。那既然是同一个线程,则都是 串行 执行的,也不会有资源竞争。那就可以不加锁。那么问题来了。递归锁存在的意义是什么?
其实仔细想下,还是为了避免多线程资源竞争的问题。
虽然 递归锁 避免同一个线程中多次加锁的死锁问题,但在多线程下,其也是一个 互斥锁
,也能处理资源竞争问题。
考虑这样一个代码
1 | - (void)method { |
如果不加锁的话,两个线程在调用 method 方法后,a 的值可能不能正常处理。当加了这个递归锁之后,可以保证多线程下 a 可以被正确处理