为什么并发队列的奇怪行为?

我试图了解iOS GCD的并发队列。 我做了一些代码来测试它,但发现了一些奇怪的东西。 代码如下:

_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); for (int index = 0; index >>> %d ",index); sleep(1); NSLog(@"sync@@@@@@ <<<< %d ",index); }); } for (int index = 3; index >>> %d ",index); sleep(1); NSLog(@"sync===== <<<< %d ",index); }); } for (int index = 6; index >>> %d ",index); sleep(1); NSLog(@"sync***** <<<< %d ",index); }); } 

执行结果如下:

 sync@@@@@@ >>>> 0 sync@@@@@@ <<<>>> 1 sync@@@@@@ <<<>>> 2 sync@@@@@@ <<<>>> 6 sync===== >>>> 4 sync===== >>>> 3 sync===== >>>> 5 sync***** <<<>>> 7 sync===== <<<< 4 sync===== <<<< 5 sync===== <<<< 3 sync***** <<<>>> 8 sync***** <<<< 8 

我很困惑,也无法理解为什么它会像这样运行。

  1. 为什么直到第一个循环完全运行然后第二个和第三个循环才能开始运行,对我来说,第一个循环应该至少可以被第二个循环中断,因为第二个循环可以创建新的线程来执行。

  2. 为什么第3个循环比第2个循环更早开始?

  3. 为什么第三个循环不能作为第一个循环运行而不会被其他任务中断?

你问:

  1. 为什么直到第一个循环完全运行然后第二个和第三个循环才能开始运行,对我来说,第一个循环应该至少可以被第二个循环中断,因为第二个循环可以创建新的线程来执行。

那是因为你使用了dispatch_sync 。 这有效地说“停止当前线程继续,直到调度任务完成”。 因此,第一个循环甚至不会进入其自身循环的下一次迭代,直到先前dispatch_sync完成的任务完成。

如果查看下图, dispatch_sync调用是红色Ⓢ标志。 你可以看到它甚至没有调度第一个循环的第二次迭代,直到第一个调度的任务完成。

  1. 为什么第3个循环比第2个循环更早开始?

这是一个经典的比赛条件。 您将大量任务分派到并发队列(所有全局队列都是并发队列),从技术上讲,它们按排队顺序启动它们,但由于它们可以同时运行,因此它们运行在在同一时间,您无法保证哪个实际上会首先到达其各自的NSLog报表。 如果你看一下与那些NSLog语句相关的时间戳是非常接近的(与第一个循环的NSLog语句不同)。

请注意,虽然您在技术上无法保证第二个或第三个循环调度的任务是否将首先启动,但有两个有趣的细节:

  1. 我们可以相对确信第三个循环的后续迭代(即迭代7和8)将不会在第二个循环的调度任务之前启动,因为同样,您将同步调度第三个循环中的所有内容。 因此,例如,它甚至不会尝试在完成迭代6的执行之前调度迭代7(而第二个循环已经异步调度其任务并且那些任务将在该并发队列上不加减少地运行)。

  2. 注意,虽然您无法保证第二个循环调度的任务的时间安排以及第三个循环调度的第一个任务,但实际上,您通常会看到第三个循环的第一个任务因为dispatch_sync内置的优化而更快地启动。 第二个循环使用的dispatch_async必须做很多工作,即GCD必须从池中获取工作线程并在该线程上启动任务。 但是作为优化,第三个循环的dispatch_sync通常只运行当前线程上的调度任务。 (如果线程必须等待已分派的任务,为什么不使用它来运行任务并完全避免上下文切换。)

    这是一个技术细节,我建议你不要担心,但确实解释了为什么你经常会看到dispatch_sync任务比几乎同时在同一个并发队列上启动的dispatch_async更快地启动。

因此,在下图中,迭代3-5(第二循环)和迭代6(第三循环的第一次迭代)的调度调用(红色Ⓢ)发生得如此接近,以至于标志叠加在彼此之上。 但是你可以看到图表下方列表中的那些时间。

  1. 为什么第三个循环不能作为第一个循环运行而不会被其他任务中断?

问题不在于第一个循环“没有中断”,而只是在队列上没有运行任何东西,并且因为它是同步运行的,所以在循环1完成之前没有其他任何东西可以启动。 而第三个循环几乎与所有第二个循环的迭代#3到5分派迭代#6。

我认为这是九个调度任务的时间表(由乐器的“兴趣点”工具制作)的说明:

在此处输入图像描述

前三个淡蓝色任务代表第一个循环。 紫色任务是第二个循环。 橙色任务是第三个循环。 dispatch_syncdispatch_async调用用红色Ⓢ标志表示。

如您所见,第一个和第三个循环显示相同的行为,即因为您同步调度这些块,它甚至无法尝试分派下一个任务,直到先前的同步调度任务完成运行。 但第二个循环运行速度非常快,一个接一个地调度所有三个任务,非常快,并且这些任务相互之间同时运行,同时主线程继续调度第三个循环,而第二个循环调度任务还在运行