【问题标题】:GCD: cancelation of blocks in global concurrent queueGCD:取消全局并发队列中的块
【发布时间】:2020-01-02 18:17:39
【问题描述】:

我读了这篇文章:GCD global concurrent queue not always concurrent(iOS device)?,这就是我想澄清的:

文档 (https://developer.apple.com/documentation/dispatch/1431058-dispatch_block_cancel) 说不能取消开始执行的任务。

建议任何分派到全局并发队列的块将自动开始执行,因此任何取消尝试都会有效地失败,这是否正确?

更具体地说 - 如果我将 1000 个任务排入队列,而每个任务需要 1000 秒才能完成,这是否意味着在 小于 1000 秒内所有任务将开始执行和取消任何任务其中会失败

iOS 和 OSX 的答案是否相同?

UPD:我想让这个问题更清楚。是否有可能对于给定数量的任务显着超出设备的计算能力,会出现这样一种情况,即我们将无法取消在全局并发队列中排队的任务,因为它们都是从哪里开始的?

【问题讨论】:

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


    【解决方案1】:

    是否建议分派到全局并发队列中的任何块将自动开始执行,因此任何取消尝试都会有效地失败?

    不完全是。首先,有点毛骨悚然:仅仅因为你已经将一些东西分派到了一个全局队列并不能保证它已经开始了。全局队列的工作线程数量有限,因此调度任务的启动时间取决于资源可用性。当然,如果队列是空闲的,那么它通常会很快启动它,但在高争用情况下,后面的一些任务可能不会立即启动。

    其次,该文档是正确的,如果取消已经开始的任务,它不会自动为您停止它。但是如果我们(并且应该)编写定期检查dispatch_block_testcancel(Swift 中的isCancelled)的任务,我们可以实现这种行为,如果是,则终止。例如:

    - (void)testDispatchItem {
        dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
    
        __block dispatch_block_t block = nil;
    
        __weak typeof(self) weakSelf = self;
    
        block = dispatch_block_create(0, ^{
            for (long i = 0; i < 10000000; i++) {
                if (dispatch_block_testcancel(block)) { break; }
                NSLog(@"%ld", i);
                [weakSelf heavyWork];
            }
    
            block = nil;
        });
    
        // start it
    
        dispatch_async(queue, block);
    
        // after five seconds, stop it if it hasn't already
    
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (block) { dispatch_block_cancel(block); }
        });
    }
    

    ...如果我将 1000 个任务排入队列...

    永远不要将这么多任务分派到全局队列,因为您将耗尽非常有限的(64 个 IIRC,尽管可能会发生变化)工作线程。这可能会导致各种问题,在某些情况下还会导致死锁。请参阅 WWDC 2015 视频 Building Responsive and Efficient Apps with GCD 中的“线程爆炸导致死锁”讨论。另请参阅 WWDC 2016 Concurrent Programming With GCD in Swift 3

    是否有可能对于给定数量的任务显着[超过]设备的计算能力,会出现这样一种情况,即我们无法取消在全局并发队列中排队的任务,因为所有任务都[已]启动?

    显然,尚未开始的任务将被取消。而对于那些已经开始的任务,只要你能正确地测试取消状态,那就不是问题了。

    【讨论】:

    • 如何检查全局队列是否已用尽或接近?
    • 我不知道。但是如果我想做一些大规模并行化的事情,我会打断戈登的结并使用dispatch_apply(在 Swift 中,concurrentPerform),它针对可用内核数量进行了优化,完全避免了这个线程爆炸问题。
    【解决方案2】:

    一旦调度任务开始运行,取消或暂停任务/队列/工作项都不会停止它。取消和暂停操作只会影响尚未开始的任务。

    在您的示例中,所有任务都已入队,但不能保证哪个任务将首先启动,尽管我们可以为每个单独的主题任务提供 QOS。

    假设我们将 1000 个任务加入队列,因为我们知道每个任务都在线程中开始,所以所有加入队列的任务不可能一起开始。如果我们假设我们有 10 个线程来执行任务,那么在 1000 个排队的项目中,10 个将开始执行,剩下的 990 个任务被排队。如果我们此时取消,则入队的 990 个任务将被取消,但已启动的 10 个任务将完成各自任务的执行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-16
      • 1970-01-01
      • 2013-10-11
      • 2012-03-25
      • 2013-10-08
      • 1970-01-01
      • 2016-04-05
      • 1970-01-01
      相关资源
      最近更新 更多