串行队列上的dispatch_async和dispatch_sync之间的区别?


125

我创建了一个这样的串行队列:

    dispatch_queue_t _serialQueue = dispatch_queue_create("com.example.name", DISPATCH_QUEUE_SERIAL);

什么之间的差异dispatch_async被称为像这样

 dispatch_async(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_async(_serialQueue, ^{ /* TASK 2 */ });

dispatch_sync在这个串行队列中这样调用?

 dispatch_sync(_serialQueue, ^{ /* TASK 1 */ });
 dispatch_sync(_serialQueue, ^{ /* TASK 2 */ });

我的理解是,无论使用哪种调度方法,TASK 1都将在执行之前完成并TASK 2正确吗?

Answers:


409

是。使用串行队列可确保任务的串行执行。唯一的区别是,dispatch_sync仅在块完成后dispatch_async返回,而将其添加到队列后可能未完成才返回。

对于此代码

dispatch_async(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_async(_serialQueue, ^{ printf("3"); });
printf("4");

它可以打印2413214312341总是前3

对于此代码

dispatch_sync(_serialQueue, ^{ printf("1"); });
printf("2");
dispatch_sync(_serialQueue, ^{ printf("3"); });
printf("4");

它总是打印 1234


注意:对于第一个代码,它不会打印1324。因为执行后被printf("3")调度。任务只有在分派才能执行。 printf("2")


任务的执行时间不变。此代码始终打印12

dispatch_async(_serialQueue, ^{ sleep(1000);printf("1"); });
dispatch_async(_serialQueue, ^{ printf("2"); });

可能发生的是

  • 线程1:dispatch_async将一个耗时的任务(任务1)同步到串行队列
  • 线程2:开始执行任务1
  • 线程1:将另一个任务(任务2)分配给串行队列
  • 线程2:任务1已完成。开始执行任务2
  • 线程2:任务2已完成。

你总是看到 12


7
它也可以打印2134和1243
Matteo Gobbi,2014年

我的问题是为什么我们不像通常那样做呢?printf("1");printf("2") ;printf("3") ;printf("4")-相较于dispatch_sync
androniennn 2014年

@androniennn是第二个例子吗?因为其他一些线程可能会同时运行dispatch_sync(_serialQueue, ^{ /*change shared data*/ });
Bryan Chen

1
@ asma22在多个线程/调度队列之间共享一个非线程安全对象非常有用。如果仅访问串行队列中的对象,则说明您正在安全地访问它。
Bryan Chen

1
我的意思是串行执行。从角度来看,所有任务都是串行执行的,这与同一队列中的其他任务有关。当然,它仍然可以并发地对待其他队列。GCD的重点是可以同时分派和执行任务。
Bryan Chen

19

dispatch_sync和之间的区别dispatch_async很简单。

在您的两个示例中,TASK 1将始终在之前执行,TASK 2因为它是在其之前调度的。

dispatch_sync但是,在该示例中,只有在分派并执行TASK 2之后TASK 1才分派。这称为“阻止”。您的代码将等待(或“阻止”)直到任务执行。

在该dispatch_async示例中,您的代码将不等待执行完成。两个块都将分派(并入队)到队列中,其余代码将继续在该线程上执行。然后在将来的某个时间(取决于已分发到队列中的其他内容)Task 1将执行,然后Task 2将执行。


2
我认为您的订单错了。第一个示例是async非阻塞版本
Bryan Chen

我已经根据您的意思对您的答案进行编辑。如果不是这种情况,请进行更改并进行说明。
JRG开发人员

1
如果您在同一队列上调用dispatch_sync,然后又调用dispatch_async,该怎么办?(反之亦然)
0xSina 2013年

1
在串行队列上,两个任务仍然一个接一个地执行。在第一种情况下,调用方等待第一个块完成,但不等待第二个块。在第二种情况下,调用者不等待第一个块完成,而是等待第二个块。但是,由于队列按顺序执行了块,因此调用者实际上等待了两者完成。
gnasher729 2014年

1
一个块还可以在其自己的队列上执行dispatch_async(添加更多的块,稍后再执行);自己的串行队列或主队列上的dispatch_sync会死锁。在这种情况下,调用方将等待原始块完成,而不会等待其他块。只需记住:dispatch_sync将块放在队列的末尾,队列执行代码直到该块完成,然后dispatch_sync返回。dispatch_async只是将块添加到队列的末尾。
gnasher729 2014年

5

这都与主队列有关。有4个排列。

i)串行队列,异步调度:这里的任务将一个接一个地执行,但是主线程(UI上的效果)不会等待返回

ii)串行队列,调度同步:这里的任务将一个接一个地执行,但主线程(UI上的效果)将显示滞后

iii)并发队列,异步调度:在这里,任务将并行执行,并且主线程(UI上的效果)将不等待返回,并且将保持平稳。

iv)并发队列,调度同步:这里任务将并行执行,但是主线程(UI上的效果)将显示滞后

您选择并发队列还是串行队列取决于您是否需要下一个任务的上一个任务的输出。如果您依赖于上一个任务,则采用串行队列,否则采用并发队列。

最后,当我们完成业务时,这是一种渗透回主线程的方法:

DispatchQueue.main.async {
     // Do something here
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.