【问题标题】:Suspending serial queue挂起串行队列
【发布时间】:2017-04-09 13:50:16
【问题描述】:

今天我尝试了以下代码:

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

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

【问题讨论】:

  • 您发布的代码无法编译。您的 if (i == 5000) { 行位于 for 循环之后。
  • 我的错,我只是凭记忆复制了它。让我解决它
  • 好的,这是您不将真实代码粘贴到浏览器中的问题之一?我不能落后。
  • @matt 我是根据我在办公室拍的照片写的。只是一个错字:)
  • 让我问你这个问题:你为什么期待任何东西?无法保证队列何时开始,特别是考虑到它是QOS_CLASS_BACKGROUND。所以你不是说串行队列从未启动吗?为什么这是一个惊喜,因为没有关于它的规则? 尤其是,因为您占用了主队列,这可能会使后台队列更不愿意开始?毕竟,实际的线程管理不取决于我们;这就是GCD的point

标签: ios objective-c grand-central-dispatch


【解决方案1】:

按照其名称,串行队列按顺序执行任务,即仅在前一个任务完成后从下一个任务开始。优先级是后台,所以当当前队列到达第 5000 个任务并挂起队列时,它甚至可能还没有开始第一个任务。

来自dispatch_suspend的文档:

在调用时运行的任何块完成后发生暂停。

也就是说,它没有保证队列中异步分派的任务会完成,只是任何当前正在运行的任务(块)都不会在中途暂停。在串行队列上最多可以有一个任务“当前正在运行”,而在并发队列上则没有指定的上限。 编辑: 根据您对一百万个任务的测试,并发队列似乎维护了它“完全并发”的概念抽象,因此即使它们实际上都认为它们“当前正在运行”不是。

要在第 5000 个任务之后暂停它,您可以从第 5000 个任务本身触发它。 (那么您可能还想从暂停时间开始启动恢复计时器,否则理论上如果恢复发生在暂停之前,它可能永远不会恢复。)

【讨论】:

  • 那么为什么并发队列会像发条一样以相同的优先级运行呢?
  • @AleksandrMedvedev 这并不重要; API 指定了您可以期望的内容,并且您不能期望串行队列以您想要的方式工作。我的猜测是,由于它是并发的,它将所有已调度的任务视为“当前正在运行”并让它们完成,而串行队列最多只考虑一个“当前正在运行”的任务。
  • @AleksandrMedvedev 如果您要使用并发队列增加任务的数量和长度,这也是一个有趣的实验。如果您派出 50,000 个并且每个人都做更多的工作,它仍然有效吗?
  • 我做到了!我测试了一百万,大约花了 2 分钟,并发队列在挂起之前正好执行了 500000 个任务
  • @AleksandrMedvedev 有趣的是,我猜队列在内部将所有这些任务视为“当前正在运行”以维护抽象(即,如果您的意图,您不必考虑实际的线程数是让所有这些任务并行执行)。
【解决方案2】:

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

【讨论】:

  • 感谢您的建议。我知道barriers,但问题与解决方案无关。我感兴趣的是它是如何工作的,以及它在挂起和并发队列方面意味着“已经在运行的任务”。无论如何,感谢您的努力。
猜你喜欢
  • 1970-01-01
  • 2013-02-24
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 1970-01-01
  • 1970-01-01
  • 2021-03-01
  • 1970-01-01
相关资源
最近更新 更多