暂停串行队列

Suspending serial queue

今天我尝试了以下代码:

- (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);

在这种情况下,随机数量的任务设法在挂起之前执行,但通常这个数字接近于零(挂起发生在任何任务之前)。 问题是 - 为什么串行队列和并发队列的挂起工作方式不同以及如何正确挂起串行队列?

正如其名,串行队列按顺序执行任务,即只有在前一个任务完成后才开始执行下一个任务。优先级 class 是后台,因此当当前队列达到第 5000 个任务并挂起队列时,它甚至可能还没有启动第一个任务。

来自 dispatch_suspend 的文档:

The suspension occurs after completion of any blocks running at the time of the call.

即,它没有任何地方承诺队列中的异步调度任务会完成,只是任何 当前 运行 任务(块)不会被部分挂起通过。在串行队列上最多一个任务可以是"currently running",而在并发队列上没有指定的上限。 编辑:根据你对一百万个任务的测试,似乎并发队列保持了它的概念抽象"completely concurrent",因此考虑了所有这些"currently running" 即使他们实际上不是。

要在第 5000 个任务之后暂停它,您可以从第 5000 个任务本身触发它。 (那你可能还想从挂起的时候开始resume-timer,否则理论上可能挂起前resume就永远不会resume了。)

我认为问题在于您将 suspendbarrier 混淆了。 suspend 现在 停止死队列。 barrier 当屏障之前队列中的所有内容都已执行时停止。因此,如果您在第 5000 个任务之后设置障碍,则在我们在串行队列的障碍处暂停之前将执行 5000 个任务。