为什么并发队列的奇怪行为? [英] why weird behavior of concurrent queue?

查看:137
本文介绍了为什么并发队列的奇怪行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to understanding the concurrent queue of iOS GCD. The I made some code to test it, but found something weird. code as below:

_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

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

I'm confused, and cannot understand why it's run like this.


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

  1. why until 1st loop runs completely finished then 2nd and 3rd loop can start to run, to me, 1st loop should can be interrupted by 2nd loop at least, since 2nd loop can create new threads to execute.

为什么第三个循环比第二个循环更早开始?

Why the 3rd loop start earlier than the 2nd loop ?

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

Why 3rd loop cannot run as the 1st loop which runs without interrupted by other tasks ?


推荐答案

您问:



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


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

That's because you've used dispatch_sync. That effectively says "stop the current thread from proceeding until the dispatched task finishes." So, that first loop won't even get to the next iteration of its own loop until the task dispatched with prior dispatch_sync finishes.

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

If you look at the diagram below, the dispatch_sync calls are the red Ⓢ flags. You can see that it doesn't even get to dispatching the second iteration of this first loop until the first dispatched task finishes.



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




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

It's a classic race condition. You're dispatching a lot of tasks to a concurrent queue (all of the global queues are concurrent queues), which technically starts them in the order that they were queued, but since they're allowed to run concurrently, they're running at the same time and you have no assurances as to which will actually reach its respective NSLog statements first. If you look at the timestamps associated with those NSLog statements are incredibly close to each other (unlike the NSLog statements of the first loop).

注意,虽然从技术上讲不确定第二个或第三个循环调度的任务是否会先启动,但有两个有趣的细节:

Note, while you technically have no assurances as to whether tasks dispatched by the second or third loop will start first, there are two interesting details:


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

  1. We can be relatively confident that the subsequent iterations of the third loop (namely iterations 7 and 8) will not start before the second loop's dispatched tasks, because again, you dispatch everything in the third loop synchronously. So, for example, it won't even try to dispatch iteration 7 until the execution of iteration 6 is done (whereas the second loop had already dispatched its tasks asynchronously and those tasks will run unabated on that concurrent queue).

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

Note, while you have no assurances about the timing of tasks dispatched by second loop and that first task dispatched by third loop, in practice, you'll generally see that first task of the third loop start more quickly because of optimizations built in to dispatch_sync. The dispatch_async used by the second loop has to do a lot of work, namely GCD must get a worker thread from the pool and and start the task on that thread. But the dispatch_sync of the third loop, as an optimization, often just runs the dispatched task on the current thread. (If the thread has to wait for the dispatched task anyway, why not just use it to run the task and avoid the context switch altogether.)

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

This is a technical detail that I'd suggest you don't worry about, but does explain why you'll often see dispatch_sync tasks start more quickly than dispatch_async started on the same concurrent queue at roughly the same time.

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

So, in the diagram below, the dispatch calls (the red Ⓢ) for iterations 3-5 (second loop) and iteration 6 (the first iteration of third loop) happen so close together that the flags are superimposed on top of each other. But you can see the timing of those in the list below the chart.



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


问题是第一个循环没有中断,但仅仅是队列中没有其他任何东西在运行,并且由于它是同步运行的,所以在循环1完成之前没有其他任何东西可以启动。而第三个循环几乎与所有第二个循环的迭代#3到5一起调度迭代#6。

The issue isn't that first loop ran "without interruption" but merely that there wasn't anything else running on the queue and because it was running synchronously, nothing else was going to start on it until loop 1 finished. Whereas the third loop dispatched iteration #6 at nearly the same time as all of the second loop's iterations #3 through 5.

我认为这是一个时间轴的说明(这些九个调度任务由Instruments的兴趣点工具生成:

I think it's illustrative to look at a timeline (produced by Instruments' "Points of Interest" tool) of these nine dispatched tasks:

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

The first three light blue tasks represent the first loop. The purple tasks are second loop. The orange tasks are the third loop. The dispatch_sync and dispatch_async calls are indicated with the red Ⓢ flags.

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

As you can see, the first and third loop show the same behavior, namely that because you're dispatching these blocks synchronously, it can't even try to dispatch the next task until the prior synchronously dispatched task finishes running. But the second loop ran incredibly quickly, dispatching all three tasks one right after the other, very quickly, and those tasks ran concurrently with respect to each other while the main thread carried on proceeded dispatching the third loop while the tasks dispatched by the second loop were still running.

这篇关于为什么并发队列的奇怪行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆