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

为何GCD异步并发队列中‘end’未穿插在循环任务中打印?

为什么“end”没有穿插在for循环的异步任务中打印?

嘿,这个问题我之前做iOS并发开发时也踩过类似的坑,咱们来一步步理清楚原因~

核心原因1:GCD队列的FIFO规则

不管是并发队列还是串行队列,GCD的任务队列都遵循**先进先出(FIFO)**的排队逻辑。你看你的代码:

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
// 先循环提交1000个打印i的任务
for (NSUInteger i = 0; i < 1000; i++) {
    dispatch_async(queue, ^{
        NSLog(@"i:%lu", (unsigned long)i);
    });
}
// 最后才提交打印end的任务
dispatch_async(queue, ^{
    NSLog(@"end:%@", [NSThread currentThread]);
});

这1000个i任务会先全部进入队列排队,end任务是最后一个加入队列的。GCD调度任务时,会优先从队列头部取任务执行,只有当前面的1000个任务都被取出队列(开始执行)后,才会轮到end任务。

核心原因2:任务提交的速度远快于执行速度

for循环提交1000个异步任务的速度几乎是瞬间完成的,等你提交end任务时,队列里已经塞满了前面的1000个任务。虽然并发队列可以同时启动多个线程执行任务,但end任务排在队列末尾,大概率要等大部分i任务都执行完(至少是开始执行)后,才会被调度到。

额外小提示:你的代码还有一个变量捕获的问题

顺带提一句,你当前的block捕获i的方式会导致很多打印的i值是重复的(比如最后可能全是999)。因为dispatch_async是异步执行,当block真正跑起来的时候,for循环可能已经执行完了,所有block捕获的都是同一个i的内存地址,最后拿到的是循环结束后的i值。如果想要每个block打印对应的循环值,需要在循环里创建一个局部变量来捕获当前的i:

for (NSUInteger i = 0; i < 1000; i++) {
    NSUInteger currentI = i; // 用局部变量捕获当前i的值
    dispatch_async(queue, ^{
        NSLog(@"i:%lu", (unsigned long)currentI);
    });
}

怎么让“end”穿插打印?

如果想让end能穿插在i的日志里,你需要把end任务的提交时机放到循环中间,比如每隔一定次数就提交一次:

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (NSUInteger i = 0; i < 1000; i++) {
    dispatch_async(queue, ^{
        NSLog(@"i:%lu", (unsigned long)i);
    });
    // 第500次循环时提交end任务
    if (i == 499) {
        dispatch_async(queue, ^{
            NSLog(@"end:%@", [NSThread currentThread]);
        });
    }
}

这样end任务会排在第500个i任务之后、剩下的500个i任务之前,GCD调度时就会让end和后面的i任务并发执行,从而出现穿插打印的效果。

内容的提问来源于stack exchange,提问作者young

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:26:27