暂停串行队列

3

今天我尝试了以下代码:

- (void)suspendTest {
    dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_BACKGROUND, 0);
    dispatch_queue_t suspendableQueue = dispatch_queue_create("test", attr);
    for (int i = 0; i <= 10000; i++) {
        dispatch_async(suspendableQueue, ^{
            NSLog(@"%d", i);
        });
        if (i == 5000) {
            dispatch_suspend(suspendableQueue);
        }
    }
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"Show must go on!");
        dispatch_resume(suspendableQueue);
    });
}

代码开始了10001个任务,但应该在执行了一半的时候暂停队列以便于在6秒后恢复运行新任务。这段代码能够按预期运行 - 5000个任务被执行,然后队列停止,6秒后它会恢复运行。 但是,如果我使用一个串行队列而不是并发队列,它的行为对我来说并不明确。
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_BACKGROUND, 0);

在这种情况下,有一些任务在挂起之前得以执行,但通常这个数量非常接近于零(在任何任务之前挂起)。 问题是 - 为什么串行队列和并发队列的挂起方式不同,如何正确地挂起串行队列?

1
你发布的代码无法编译。你的 if (i == 5000) { 行在 for 循环之后。 - rmaddy
2
好的,这是那种你不会把真正的代码粘贴到浏览器中的问题吗?我不能理解。 - matt
1
让我问你这个问题:你为什么期望什么呢?关于队列何时开始没有任何保证,特别是当它是QOS_CLASS_BACKGROUND时。所以你不是在说串行队列从未启动吗?既然没有规则,为什么会感到惊讶呢?而且尤其是因为你正在占用主队列,这可能会使后台队列更不愿意启动?毕竟,实际的线程管理不由我们决定;这就是GCD的重点。 - matt
据我所知,有一个规则,即任何已排队的块都应该被执行,而任何在暂停后的块都应该等待恢复。 - The Dreams Wind
有一个规则,任何已经排队的块都应该被执行。最终它会被执行。但是在串行队列上挂起后,它不会被执行,因为我们甚至不能开始,更别说执行了。这就是串行的意思。 - matt
显示剩余3条评论
2个回答

2
根据其名称,串行队列按顺序执行任务,即仅在前一个任务完成后才开始下一个任务。优先级类别为background,因此当当前队列达到第5000个任务并暂停队列时,甚至可能尚未启动第一个任务。
dispatch_suspend的文档中可以看出:
挂起发生在调用时正在运行的任何块完成之后。
也就是说,它没有保证队列上异步派发的任务会完成,只保证不会在当前正在运行的任务(块)中途挂起。串行队列最多只能有一个“当前运行”的任务,而并发队列没有指定的上限。编辑:根据您对一百万个任务的测试,似乎并发队列维护了概念抽象,认为所有任务都是“完全并发”的,即使它们实际上并非如此。
要在第5000个任务后将其暂停,您可以从第5000个任务本身触发此操作。(然后您可能还想从暂停时开始启动恢复计时器,否则理论上可能永远无法恢复,如果恢复发生在挂起之前的话。)

那么为什么并发队列在相同优先级下能像钟表一样运行呢? - The Dreams Wind
1
@AleksandrMedvedev并不重要;API指定了您可以期望什么以及您不能期望串行队列按您希望的方式工作。 我的猜测是,由于它是并发的,它将所有已调度的任务都视为“正在运行”,并让它们完成,而串行队列仅将最多一个任务视为“正在运行”。 - Arkku
@AleksandrMedvedev 如果您增加并发队列的任务数量和长度,那么这也将是一个有趣的实验。如果您派遣50,000个任务并且每个任务都做更多的工作,它是否仍然有效? - Arkku
1
我做到了!我进行了一百万次测试,大约花了2分钟,同时执行队列在挂起之前精确地执行了500000个任务。 - The Dreams Wind
有趣,我想队列在内部将所有这些任务视为“当前正在运行”,以维护抽象(即,如果您的意图是让所有这些任务并行运行,则无需考虑实际线程数量)。 - Arkku

0
我认为问题在于你混淆了suspendbarriersuspend立刻停止队列, 而barrier在队列中的所有任务在执行完之后停止。因此,如果你在第5000个任务后添加一个障碍,那么在串行队列上暂停之前将执行5000个任务。

谢谢您的建议。我知道barrier,但问题与解决方案无关。我对它的工作原理感兴趣,以及在挂起和并发队列方面,“已运行的任务”是什么意思。无论如何,我很感激您的努力。 - The Dreams Wind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接