dispatch_async 嵌套是如何工作的?

How does dispatch_async nesting work?

我正在尝试理解这种常见模式:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        // Background stuff on background thread
        dispatch_async(dispatch_get_main_queue()) {
            // Update UI on main thread
        }
    }

苹果文学states:

Completion callbacks can be accomplished via nested calls to the dispatch_async() function.

好的,但我认为 dispatch_async 的 FIFO 方面是它保证任务 开始 提交的顺序。我以为它不能保证它们会按任何顺序完成?

我的问题是,为什么 嵌套调用要等待其嵌套的 closure/block 完成?

如果我写

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        doThing1()
        doThing2()
        doThing3()
}

那能保证doThing2()会等到doThing1()执行完才执行吗?如果是这样,这是否意味着它等同于两个后续的 dispatch_sync 调用,如下所示:

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    doThing1()
}

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    doThing2()
}

?

Ok, but I thought the FIFO aspect of dispatch_async was that it guarantees that tasks start in the order submitted. I thought it didn't guarantee that they would complete in any order?

你是对的,但这也取决于这是串行队列还是并发队列。对于串行队列,条目的顺序就是完成的顺序:在队列中的前面的项目完成之前不会发生任何新事情。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        doThing1()
        doThing2()
        doThing3()
}

在那段代码中,它是什么类型的队列并不重要。您的代码都在一个块中。该块中事物的顺序不会被神奇地打乱!这会使整个编程概念变得毫无意义。

但是,其中 none 了解 Apple 在您引用的句子中的意思。嵌套完成块对于事情的工作方式至关重要,因为它们意味着您可以从主线程进入后台线程,做一些事情,然后在完成后返回主线程并做进一步的事情。这是按顺序发生的。这保证您可以遵守不得在后台线程上触摸界面的重要规则。此外,在这种安排下,来自周围区块的局部变量是可见的,因此数据可以安全地从一个区块传递到另一个区块,就像我书中的这个例子:

- (void) drawThatPuppy {
    CGPoint center = 
        CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
    dispatch_async(self->_draw_queue, ^{ 
        CGContextRef bitmap = [self makeBitmapContext: self.bounds.size];
        [self drawAtCenter: center zoom: 1 context:bitmap];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (self->_bitmapContext)
                CGContextRelease(self->_bitmapContext);
            self->_bitmapContext = bitmap;
            [self setNeedsDisplay];
        });
    });
}