为何这段GCD代码无法正常运行?子线程卡在__ulock_wait但未死锁
为什么这段GCD代码会卡在__ulock_wait状态?
这问题我之前碰到过类似的,核心是GCD并发队列的线程池限制 + 任务提交速率过高导致的资源耗尽型等待,不是传统意义上的互斥死锁,咱们拆解来看:
代码逻辑回顾
先看你写的核心代码:
dispatch_queue_t queue = dispatch_queue_create("test_gcd_queue", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10000; i++) { dispatch_async(queue, ^{ dispatch_sync(queue, ^{ NSLog(@"---- gcd: %d", i); }); }); //NSLog(@"---------- async over: %d", i); //添加此代码则正常运行。 } NSLog(@"-------------------- cycle over");
你创建了一个并发队列,然后循环10000次,每次用dispatch_async把一个任务扔到并发队列;这个异步任务里又立刻用dispatch_sync往同一个并发队列扔了一个同步任务,等着它执行完。
卡住的根本原因
GCD的并发队列背后是有限大小的线程池(默认大小和CPU核心数相关,比如4核设备默认是4个工作线程)。当你在主线程疯狂提交10000个dispatch_async任务时,GCD会快速把线程池里的所有线程都占满:
- 每个工作线程拿到外层的async任务后,立刻执行
dispatch_sync——这个操作要求当前队列必须有空闲线程来执行这个同步任务,否则当前线程会进入等待状态(也就是你看到的__ulock_wait)。 - 但此时线程池里的所有线程都在执行外层的async任务,并且都卡在
dispatch_sync的等待上,没有任何空闲线程能去处理那些排队的sync任务。 - 这就形成了一个死循环:所有工作线程都在等新的线程来处理自己的sync任务,但线程池已经没有多余线程了,GCD也不会无限创建线程(避免系统资源耗尽),于是所有线程都卡在等待状态。
为什么加了NSLog就正常?
你注释掉的那个NSLog(@"---------- async over: %d", i);是关键:
NSLog是个相对耗时的操作,它内部会加锁、格式化字符串、输出到控制台,这会减慢主线程提交async任务的速率。- 当提交速率变慢后,GCD有足够的时间在主线程提交下一个async任务前,让之前的工作线程完成sync任务并释放线程。线程池不会被瞬间占满,自然不会出现所有线程都等待的情况。
额外补充
这种情况和传统的互斥死锁不同——没有两个线程互相持有对方需要的锁,而是所有可用的执行资源都被占用,且占用资源的任务都在等待新的资源,属于资源耗尽导致的任务阻塞。
内容的提问来源于stack exchange,提问作者fyxrhyry
相关产品推荐
相关产品推荐

