GCD:取消全局并发队列中的块

GCD: cancelation of blocks in global concurrent queue

我读了这篇文章: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:我想让这个问题更清楚。对于给定数量的任务,是否可能会出现显着超出设备计算能力的情况,我们将无法取消在全局并发队列中排队的任务,因为它们都是从哪里开始的?

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

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

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

is it correct to suggest that any block dispatched into the global concurrent queue will automatically start the execution and thus any attempt of cancellation will effectively fail?

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

其次,该文档是正确的,如果取消一项已经开始的任务,它不会自动为您停止。但是如果我们(并且应该)编写定期检查 Swift 中的 dispatch_block_testcancel (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); }
    });
}

... if I enqueue 1000 tasks ...

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

Is it possible that for a given number of tasks significantly [exceeding] computational capabilities of the device there will be a situation when we will fail to cancel tasks queued in the global concurrent queue because all of them [were] started?

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