如果我有嵌套的dispatch_async调用,会发生什么?

23

这可能是一个愚蠢的问题,但我需要问一下并为自己澄清一下。

要将一个块提交到队列以进行执行,请使用函数dispatch_syncdispatch_async。它们都接受队列和块作为参数。dispatch_async立即返回,异步运行块,而dispatch_sync阻止执行,直到提供的块返回为止。以下是一些情况:

情况1

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);    
dispatch_async(queue, ^{
    [self goDoSomethingLongAndInvolved];
    dispatch_async(queue, ^{
        NSLog(@"this is statement1");

    });
});

情况2

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);    
dispatch_sync(queue, ^{
    [self goDoSomethingLongAndInvolved];
    dispatch_sync(queue, ^{
        NSLog(@"this is statement1");

    });
});

情境 3

{
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);    
    dispatch_async(queue, ^{
        [self goDoSomethingLongAndInvolved];
        dispatch_sync(queue, ^{
            NSLog(@"this is statement1");

        });
    });

情境 4

{
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);    
    dispatch_sync(queue, ^{
        [self goDoSomethingLongAndInvolved];
        dispatch_async(queue, ^{
            NSLog(@"this is statement1");

        });
    });

goDoSomethingLongAndInvolved

-(void)goDoSomethingLongAndInvolved {
    NSLog(@"goDoSomethingLongAndInvolved");
}

我尝试在Xcode中运行它们,但是我根本看不出任何区别。

所以我的问题是:

  1. 这些情况的主要区别是什么?
  2. 如果我使用dispatch_get_main_queue()替换queue会怎样?

1
当你说“你看不出区别”的时候,你是指什么?执行速度上的区别吗?考虑到你的 goDoSomethingLongAndInvolved 方法只需要几纳秒,如果你看不到速度差异我也不感到意外。 - davidf2281
1
你尝试在块内外设置断点了吗? - Sebastian
写长篇文章vs阅读苹果文档,1:0,但是......第二个更容易。http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/dispatch_sync.3.html - holex
1个回答

18

dispatch_sync会等待覆盖它的代码块完全执行。而dispatch_async则会立即返回并继续执行下一行代码,所以里面的所有内容都是并发执行的。

如果queue是由您自己创建的串行队列,则:

情况1-根代码块会立即返回。在其中等待[self go....],然后转到dispatch_async,它也会立即返回。

情况2-如果queue是串行队列,则会死锁,因为它将等待自己完成执行。由于您正在处理异步执行块,因此该块将并行执行。(感谢@Ken Thomases)

情况3-这里不需要使用dispatch_sync,因为它会导致死锁。

情况4-等待[self ...],然后立即返回。

如果您将queue替换为主队列,请记住不要在主队列上使用dispatch_sync,因为这会导致死锁(如果不是从主线程调度,则不会死锁,感谢@Ken Thomases)。

为了更好地理解,请将您的函数替换为:

-(void)goDoSomethingLongAndInvolved:(NSString *)message {
    for(int i = 0; i < 50; ++i) {
        NSLog(@"%@ -> %d", message, i); 
    }
}

无论是等待还是不等待,你都能清楚地看到发生了什么。祝你好运。


@ dreamzor:所有的情况都很清楚,但是第二种情况比较特殊。外部的 dispatch_sync 将等待其块完成。首先,goDoSomethingLongAndInvoled 完成(将显示出 goDoSomethingLongAndInvolved),然后是内部的 dispatch_sync。就像所说的那样,它将等待 NSLog(@"this is statement1") 完成。现在 this is statement 将被打印出来。因此,dispatch_sync 完成了。最终,外部的 dispatch_sync 也完成了。这就是为什么我困惑为什么会在这里导致死锁。 - tranvutuan
5
情况2并不会导致死锁。如果“队列”是串行队列,那么会导致死锁,但实际上并非如此。因此,即使外部任务仍在进行中,内部任务也可以运行。而且,dispatch_sync()到主队列并不总是会死锁。只有它是从主线程分派过来的时才会死锁。这将使主线程等待自己。 - Ken Thomases
你们是对的,我很抱歉。我通常会创建自己的串行“队列”,所以这是基于这样的经验 :) - dreamzor
2
如果您在答案中更正有关情况2的说法,我会撤销我的负评。 - Ken Thomases
1
@Elsammak 它在队列中等待,直到调用下一个异步调用 - 在队列中没有异步调用来调用[self go ...]本身。 - dreamzor
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接