You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何这段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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:10:55