在条件循环中调度队列时出现问题


-(void) sample
{
    dispatch_queue_t aQueue = dispatch_queue_create("hello-world", NULL);
    __block  int j=0;
    for(int i=0; i<3; ++i){
        dispatch_sync(aQueue, ^{
            j++;
            NSLog(@"I'm in Loopn");
            if(j==2)
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"I'm in main for the first timen");
                });
        });
    }
    dispatch_sync(aQueue, ^{
        NSLog(@"I'm in the second task of aQueuen");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"I'm just about to exit from the main threadn");
        });
    });
}

输出:

2016-02-02 17:11:16.226 facebookCustom Post[5078:227956] I'm in Loop
2016-02-02 17:11:16.227 facebookCustom Post[5078:227956] I'm in Loop
2016-02-02 17:11:16.227 facebookCustom Post[5078:227956] I'm in Loop
2016-02-02 17:11:16.227 facebookCustom Post[5078:227840] I'm in the second task of aQueue
2016-02-02 17:11:16.426 facebookCustom Post[5078:227840] I'm in main for the first time
2016-02-02 17:11:16.426 facebookCustom Post[5078:227840] I'm just about to exit from the main thread

代码的输出对我来说非常令人惊讶,因为在第一个任务完成之前不应该执行任务,因为我们第一次同步调度队列,对吗?那么I'm in the second task of aQueue怎么能在I'm in main for the first time之前打印出来呢?

我相信

这个问题的答案中得到了更完整的回答,并且与GCD实现中的优化有关。

这些块在当前(主(线程上执行,而不是在 aQueue 上执行,作为一种优化,因此,任何通过dispatch_get_main_queue()的调用都被"排队"(原谅双关语(稍后执行,我认为在运行循环的下一次迭代中。

您可以通过使用 NSLog() 而不是 printf() 进行日志记录来获取更多信息,因为这将打印线程 ID。 请使用该输出更新您的问题。

这就是我现在所拥有的一切,也许@bbum会摆动并用更好的答案澄清。

顺便说一句,这是一个很好的问题。

如前所述,发生这种情况是因为您正在阻塞主线程,dispatch_syncaQueue,因此您对主线程的第一NSLog被推迟到您的方法完成运行之后。

此外,你对此的做法是完全错误的。在大多数情况下,将dispatch_sync从另一个串行队列使用到串行队列是完全多余的,因为原始队列将被阻止运行,直到另一个串行队列完成。它还存在创建线程死锁的风险,因此您应该尽可能使用dispatch_async

正如这个答案所说,当使用dispatch_sync从主线程到另一个串行队列时,在大多数情况下,GCD 无论如何都会在主线程上运行它,因为传输它的成本很高。

但是,我不确定您在这里使用 GCD 想要实现什么目的。您拥有的代码并不比在主线程上运行所有内容更有效(如果不是更少的话((因为无论如何,这是最有可能在幕后发生的事情(。

如果您只是尝试将任务卸载到单独的线程上,那么您希望使用 dispatch_async .

此外,如果您希望输出以可靠的顺序发生,则可以对主线程使用dispatch_sync(是的,我在这里对我的规则做了例外,但只是因为它是单个NSLog(,从而保证第一个主线程日志发生在下一个之前(并且不会导致死锁,因为您正在使用dispatch_asyncaQueue(。

-(void) sample
{
    dispatch_queue_t aQueue = dispatch_queue_create("hello-world", DISPATCH_QUEUE_SERIAL); // I changed the attr parameter from NULL to DISPATCH_QUEUE_SERIAL for increased readability.
    __block  int j=0;
    for(int i=0; i<3; ++i){
        dispatch_async(aQueue, ^{
            j++;
            NSLog(@"I'm in Loopn");
            if(j==2)
                dispatch_sync(dispatch_get_main_queue(), ^{
                    NSLog(@"I'm in main for the first timen");
                });
        });
    }
    dispatch_async(aQueue, ^{
        NSLog(@"I'm in the second task of aQueuen");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"I'm just about to exit from the main threadn");
        });
    });
}

输出现在为:

2016-02-02 12:06:46.573 gcd test again[2351:2465744] I'm in Loop
2016-02-02 12:06:46.573 gcd test again[2351:2465744] I'm in Loop
2016-02-02 12:06:46.580 gcd test again[2351:2465707] I'm in main for the first time
2016-02-02 12:06:46.581 gcd test again[2351:2465744] I'm in Loop
2016-02-02 12:06:46.581 gcd test again[2351:2465744] I'm in the second task of aQueue
2016-02-02 12:06:46.581 gcd test again[2351:2465707] I'm just about to exit from the main thread

这样,主线程可以继续运行,只会中断以确保您的NSLog调用以正确的顺序调度。

但是,我永远不会建议使用主队列">只是为了同步一些调用"。实际上,您应该为此使用单独的串行队列。

我希望这是有道理的。即使在最好的时候,GCD也会令人困惑!

因为 printf 不需要在调用时准确地将字符放在屏幕上。每个线程都可以收集 printf 输出,并在需要时将其放在屏幕上。这通常更有效率。

在 printf 调用上设置断点,调用将按预期顺序执行。 printf 不定义从多个线程调用时的输出顺序。

最新更新