在条件循环中调度队列的问题

Trouble with dispatching queues in a conditional loop

-(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 Loop\n");
            if(j==2)
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"I'm in main for the first time\n");
                });
        });
    }
    dispatch_sync(aQueue, ^{
        NSLog(@"I'm in the second task of aQueue\n");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"I'm just about to exit from the main thread\n");
        });
    });
}

输出:

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 main for the first time之前打印出I'm in the second task of aQueue呢?

我相信在 this question 的答案中可以更完整地回答这个问题,并且与 GCD 实现中的优化有关。

作为优化,这些块正在当前(主)线程上执行,而不是在 aQueue 上执行,因此通过 dispatch_get_main_queue() 的任何调用都是 "queued up"(请原谅双关语)稍后执行,我认为在 运行-loop.

的下一次迭代中

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

这就是我目前所知道的全部内容,也许@bbum 会路过并用更好的答案进行澄清。

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

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

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

,这是因为你用 dispatch_syncaQueue 阻塞了主线程,因此你的第一个 NSLog 到主线程被推迟到你的方法完成后 运行ning.

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

As said by this answer,当从主线程使用 dispatch_sync 到另一个串行队列时,在大多数情况下,GCD 只会在主线程上 运行 这个,因为传输很昂贵它。

但是我不确定您在这里使用 GCD 试图达到什么目的。您拥有的代码不比 运行 将所有内容都放在主线程上(因为这是最有可能在幕后发生的事情)。

如果您只是想将任务卸载到单独的线程上,那么您需要使用 dispatch_async.

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

-(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 Loop\n");
            if(j==2)
                dispatch_sync(dispatch_get_main_queue(), ^{
                    NSLog(@"I'm in main for the first time\n");
                });
        });
    }

    dispatch_async(aQueue, ^{
        NSLog(@"I'm in the second task of aQueue\n");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"I'm just about to exit from the main thread\n");
        });
    });
}

现在的输出是:

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 也会令人困惑!