OGeek|极客世界-中国程序员成长平台

标题: ios - 为什么并发队列的行为怪异? [打印本页]

作者: 菜鸟教程小白    时间: 2022-12-13 15:56
标题: ios - 为什么并发队列的行为怪异?

我正在尝试了解iOS GCD的并发队列。
我编写了一些代码对其进行测试,但发现有些奇怪。
代码如下:

_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

for (int index = 0; index < 3; ++index) {
    dispatch_sync(_syncQueue, ^{

        NSLog(@"sync@@@@@@ >>>>    %d ",index);
        sleep(1);
        NSLog(@"sync@@@@@@ <<<<    %d ",index);
    });
}

for (int index = 3; index < 6; ++index) {
    dispatch_async(_syncQueue, ^{

        NSLog(@"sync===== >>>>    %d ",index);
        sleep(1);
        NSLog(@"sync===== <<<<    %d ",index);
    });
}

for (int index = 6; index < 9; ++index) {
    dispatch_sync(_syncQueue, ^{

        NSLog(@"sync***** >>>>    %d ",index);
        sleep(1);
        NSLog(@"sync***** <<<<    %d ",index);
    });
}

执行结果如下:
sync@@@@@@ >>>>    0
sync@@@@@@ <<<<    0
sync@@@@@@ >>>>    1
sync@@@@@@ <<<<    1
sync@@@@@@ >>>>    2
sync@@@@@@ <<<<    2
sync***** >>>>    6
sync===== >>>>    4
sync===== >>>>    3
sync===== >>>>    5
sync***** <<<<    6
sync***** >>>>    7
sync===== <<<<    4
sync===== <<<<    5
sync===== <<<<    3
sync***** <<<<    7
sync***** >>>>    8
sync***** <<<<    8

我很困惑,无法理解为什么会这样运行。
  • 为什么直到第一个循环完全完成才可以运行第二个和第三个循环,对我来说,第一个循环至少应该可以被第二个循环打断,因为第二个循环可以创建新的线程来执行。
  • 为什么第三个循环比第二个循环更早开始?
  • 为什么第三个循环不能像第一个循环那样运行,而没有其他任务中断?


  • Best Answer-推荐答案


    你问:

  • 为什么直到第一个循环完全完成才可以运行第二个和第三个循环,对我来说,第一个循环至少应该可以被第二个循环打断,因为第二个循环可以创建新的线程来执行。

  • 那是因为您使用了dispatch_sync。这实际上表示“停止当前线程,直到分派的任务完成为止”。因此,在使用先前的dispatch_sync调度的任务完成之前,第一个循环甚至不会到达其自身循环的下一个迭代。
    如果您看下面的图,dispatch_sync调用是红色的Ⓢ标志。您可以看到,直到第一个被分配的任务完成后,才开始分配第一个循环的第二个迭代。
  • 为什么第三个循环比第二个循环更早开始?

  • 这是经典的比赛条件。您正在将大量任务分派到并发队列(所有全局队列都是并发队列),从技术上讲,这是按照它们被排队的顺序启动它们的,但是由于允许它们并发运行,所以它们在同时,您无法保证哪个会首先真正到达其各自的NSLog语句。如果您查看与这些NSLog语句相关联的时间戳,它们之间的距离非常接近(与第一个循环的NSLog语句不同)。
    请注意,尽管从技术上来讲,您无法确定第二个循环还是第三个循环调度的任务将首先开始,但是有两个有趣的细节:
  • 我们可以相对确信,第三个循环的后续迭代(即迭代7和8)不会在第二个循环的分派任务之前开始,因为同样,您将同步分派第三个循环中的所有内容。因此,例如,它甚至不会尝试调度迭代7,直到完成迭代6的执行为止(而第二个循环已经异步地调度了它的任务,并且那些任务将在该并发队列上不受影响地运行)。
  • 注意,尽管您无法保证第二个循环调度的任务的时间安排以及第三个循环调度的第一个任务的执行时间,但实际上,您通常会看到,由于内置的​​优化,第三个循环的第一个任务的启动速度更快dispatch_sync。第二个循环使用的dispatch_async必须做很多工作,即GCD必须从池中获取一个工作线程并在该线程上启动任务。但是,作为优化,第三个循环的dispatch_sync通常只是在当前线程上运行分派的任务。 (如果线程无论如何都要等待分派的任务,为什么不只使用它来运行任务并完全避免上下文切换。)
    这是一个技术细节,我建议您不要担心,但确实可以解释为什么您通常会看到dispatch_sync任务比在大约同一时间在同一并发队列上启动的dispatch_async更快地启动。

  • 因此,在下图中,迭代3-5(第二个循环)和迭代6(第三个循环的第一次迭代)的调度调用(红色happen)发生得非常紧密,以至于标记彼此叠加。但是您可以在图表下方的列表中看到这些内容的时间安排。
  • 为什么第3个循环不能像第一个循环那样运行而没有其他任务中断?

  • 问题不在于第一个循环“没有中断”地运行,而是队列中没有其他任何东西在运行,并且因为它是同步运行的,所以直到循环1完成之前,其他任何事情都不会开始。而第三个循环几乎与第二个循环的所有第3到第5个迭代同时调度了第6个迭代。
    我认为看一下这九个调度任务的时间表(由Instruments的“Points of Interest”工具生成)是说明性的:
    enter image description here
    前三个浅蓝色任务代表第一个循环。紫色任务是第二循环。橙色任务是第三个循环。用红色Ⓢ标志指示dispatch_syncdispatch_async调用。
    如您所见,第一个和第三个循环显示相同的行为,即由于您正在同步分派这些块,因此它甚至无法尝试分派下一个任务,直到前一个同步分派的任务完成运行为止。但是第二个循环运行得非常快,非常快,一个接一个地分派了所有三个任务,非常快,并且这些任务彼此并发运行,而执行的主线程继续分派了第三个循环,而第二个循环分派了任务仍在运行。

    关于ios - 为什么并发队列的行为怪异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49538333/






    欢迎光临 OGeek|极客世界-中国程序员成长平台 (http://jike.in/) Powered by Discuz! X3.4