133

I've created a serial queue like this:

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

What's the difference between dispatch_async called like this

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

And dispatch_sync called like this on this serial queue?

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

My understanding is that, regardless of which dispatch method is used, TASK 1 will be executed and completed before TASK 2, correct?

JRG-Developer
  • 12,454
  • 8
  • 55
  • 81

3 Answers3

425

Yes. Using serial queue ensure the serial execution of tasks. The only difference is that dispatch_sync only return after the block is finished whereas dispatch_async return after it is added to the queue and may not finished.

for this code

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

It may print 2413 or 2143 or 1234 but 1 always before 3

for this code

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

it always print 1234


Note: For first code, it won't print 1324. Because printf("3") is dispatched after printf("2") is executed. And a task can only be executed after it is dispatched.


The execution time of the tasks doesn't change anything. This code always print 12

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

What may happened is

  • Thread 1: dispatch_async a time consuming task (task 1) to serial queue
  • Thread 2: start executing task 1
  • Thread 1: dispatch_async another task (task 2) to serial queue
  • Thread 2: task 1 finished. start executing task 2
  • Thread 2: task 2 finished.

and you always see 12

Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
  • 9
    it can also print 2134 and 1243 – Matteo Gobbi Mar 22 '14 at 14:37
  • my question is why not we didn't do it like the normal way? `printf("1");printf("2") ;printf("3") ;printf("4")` - compared to `dispatch_sync` – androniennn Apr 12 '14 at 11:31
  • @androniennn for second example? because some other thread may running `dispatch_sync(_serialQueue, ^{ /*change shared data*/ });` at same time. – Bryan Chen Apr 13 '14 at 02:04
  • @BryanChen would single-threaded application benefit from `dispatch_sync`? – Nik Aug 06 '14 at 20:35
  • @dig I don't think so. It is also likely to cause dead lock. – Bryan Chen Aug 06 '14 at 21:49
  • @MatteoGobbi u forgot 1324, not good when trying to be a besserwisser like that;) – DevilInDisguise Mar 24 '15 at 22:03
  • @DevilInDisguise Why would it print 1324? – somtingwong Aug 10 '15 at 17:30
  • @somtingwong it probably wouldn't, but its possible – DevilInDisguise Aug 13 '15 at 13:08
  • @DevilInDisguise Unless I'm mistaken `printf("2")` is always executed before `printf("3"). Its the first line of code hit when the first dispatch returns. Unless someone can explain otherwise, I'm positive 2 will print before 3 – somtingwong Aug 13 '15 at 13:12
  • @somtingwong Good point indeed, I am trying to understand the dispatch, and so far I would agree that 2 will print before 3. If there is a chance for 3 to be printed before 2 please explain, I do not want to be confused at this point. – IamMashed Nov 09 '15 at 18:16
  • Let's say we were `dispatch_sync(_serialQueue, ^{ printf(timeConsuimingSomething); });` then `dispatch_sync(_serialQueue, ^{ printf(notTimeConsumingSomething); });` Yet still `timeConsumingSomething` gets printed first right? How and what is it that is **holding** `notTimeConsumingSomething` from being printed first, **while** it's code is already finished before `timeConsumingSomething` !? – mfaani Apr 07 '16 at 10:37
  • @asma22 Please note the difference between enqueue the task and execute the task. Because both `dispatch_sync` is called on a same thread, it is guarantee that both tasks will be enqueued with correct order. Then dispatch serial queue will ensure the tasks are executed with correct order. – Bryan Chen Apr 07 '16 at 21:33
  • Thanks. I am sorry meant **async** , I understand the `sync` situation Let's say we were `dispatch_sync(_serialQueue, ^{ printf(timeConsuimingSomething); });` then `dispatch_sync(_serialQueue, ^{ printf(notTimeConsumingSomething); });` Yet still `timeConsumingSomething` gets printed first right? How and what is it that is **holding** `notTimeConsumingSomething` from being printed first, **while** it's code is already finished before `timeConsumingSomething` !? *if possible can you please add a case where the passing elements have very different timing, like my question? – mfaani Apr 08 '16 at 03:01
  • Super Thanks, I *think* I get it now. I just don't get what is the benefit/ use case of doing it this way or the other. I mean if the code is still to be executed in a serial way AND that no task would begin unless its previous task is finished then this is just like a serial queue performance-wise--Unless you are to compare this with something out of dispatchs effect similar to what you do did with **printf("2");** & **printf("4");** – mfaani Apr 08 '16 at 03:34
  • 1
    @asma22 It is very useful to share a non-thread safe object between multiple threads/dispatch queues. If you only access the object in a serial queue, you know you are accessing it safely. – Bryan Chen Apr 08 '16 at 03:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/108618/discussion-between-asma22-and-bryan-chen). – mfaani Apr 08 '16 at 10:31
  • Shouldn't your first line be "Using serial queue ensure the serial **enqueueing** of tasks"? – mfaani Apr 21 '16 at 02:31
  • @asma22 You can enqueue tasks to serial queue concurrently from multiple threads. – Bryan Chen Apr 21 '16 at 02:48
  • @BryanChen see [this](http://stackoverflow.com/questions/5026043/concurrency-and-serial-queues-in-grand-central-dispatch) the whole point of an asynchronous dispatch is to **enqueue** the block and immediately return. If the block was enqueued on a serial queue, it may be many minutes before it is executed. Asynchronous dispatch specifically decouples enqueueing from execution. – – mfaani Apr 21 '16 at 02:52
  • My Problem is I think your wording is incorrect. You should correct your first line to: "Using serial queue ensure the serial **enqueueing** of tasks" . Serial queue ONLY ensures **serial enqueuing** if the task is `sync` then its waits for execution and if its `async` it doesn't wait for execution. You said **serial execution**, which isn't the case for async. The serial-ness of of a serial queue is about the tasks of the queue itself since its still concurrent to other queues. – mfaani Apr 21 '16 at 03:56
  • 1
    I do mean __serial execution__. In point of view that all tasks are executed serial regards to other tasks in the same queue. Of cause it still can be concurrent regards to other queues. It is the whole point of GCD that tasks can be dispatched and executed concurrently. – Bryan Chen Apr 21 '16 at 04:26
  • Clean & Neat Explanation. Thanx for saving me from millions of time. – Balasubramanian Jul 26 '16 at 07:20
  • still learning and coming back to this post after 7 months... *It is very useful to share a non-thread safe object between multiple threads/dispatch queues. If you only access the object in a serial queue, you know you are accessing it safely* <-- access you only mean for write-read, otherwise read-read is non-problematic and good solution is to use `dispatch_barrier_async` as mentioned [here](http://stackoverflow.com/a/28784770/5175709) right? – mfaani Jan 05 '17 at 22:35
  • Yes access I mean read/write. The answer you linked is an optimization that similar to [readers-writer lock](https://en.wikipedia.org/wiki/Readers–writer_lock). So if most of the operations are readonly, it will improve performance. – Bryan Chen Jan 06 '17 at 01:10
19

The difference between dispatch_sync and dispatch_async is simple.

In both of your examples, TASK 1 will always execute before TASK 2 because it was dispatched before it.

In the dispatch_sync example, however, you won't dispatch TASK 2 until after TASK 1 has been dispatched and executed. This is called "blocking". Your code waits (or "blocks") until the task executes.

In the dispatch_async example, your code will not wait for execution to complete. Both blocks will dispatch (and be enqueued) to the queue and the rest of your code will continue executing on that thread. Then at some point in the future, (depending on what else has been dispatched to your queue), Task 1 will execute and then Task 2 will execute.

JRG-Developer
  • 12,454
  • 8
  • 55
  • 81
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • 2
    I think your get wrong order. first example is `async` which is the non-blocking version – Bryan Chen Nov 06 '13 at 21:07
  • I've edited your answer to *what I think you meant*. If this is not the case, please change it and clarify. – JRG-Developer Nov 06 '13 at 21:11
  • 1
    What if you call dispatch_sync and then dispatch_async on the same queue? (and vice versa) – 0xSina Nov 29 '13 at 16:28
  • 1
    On a serial queue, both tasks are still executed one after the other. In the first case, the caller waits for the first block to finish but doesn't wait for the second block. In the second case, the caller doesn't wait for the first block to finish, but waits for the second block. But since the queue executes the blocks in order, the caller effectively waits for both to finish. – gnasher729 Mar 20 '14 at 19:26
  • 1
    A block could also do a dispatch_async on its own queue (adding further blocks which will be executed later); dispatch_sync on the own serial queue or main queue would deadlock. In this situation, the caller will wait for the original block to finish, but not for the other blocks. Just remember: dispatch_sync puts the block at the end of the queue, the queue executes code until that block is finished, and then dispatch_sync returns. dispatch_async just adds the block at the end of the queue. – gnasher729 Mar 20 '14 at 19:28
  • what's the benefit of async dispatching/enqueueing if you still have to wait for execution to complete? Can you give a real use case for serial queue + async and serial queue + sync? – mfaani Apr 08 '16 at 10:45
  • Another important behavior to consider in dispatch_sync, and dispatch_async on serial queues is nested asynchronous behavior from within them. For example, if you call an asynchronous method from a dispatch_sync or dispatch_async on a serial queue, how will the execution be transferred. – SayeedHussain Jan 20 '17 at 12:30
  • Thank you very much. Its now working like a charm. Created a pull request for the source of the pod. the last problem was this: https://github.com/mickeyl/LTSupportAutomotive/pull/26/commits/03992d4652c11c9acfdaf4a11b99403657c6e6ee Thank you :) – Skyborg Mar 21 '19 at 12:12
7

It is all related to main queue. There are 4 permutations.

i) Serial queue, dispatch async : Here the tasks will execute one after the other, but the main thread(effect on UI) will not wait for return

ii) Serial queue, dispatch sync: Here the tasks will execute one after the other, but the main thread(effect on UI) will show lag

iii) Concurrent queue, dispatch async : Here the tasks will execute in parallel and the main thread(effect on UI ) will not wait for return and will be smooth.

iv) Concurrent queue, dispatch sync : Here the tasks will execute in parallel, but the main thread(effect on UI) will show lag

Your choice of concurrent or serial queue depends on if you need an output from a previous task for the next one. If you depend on the previous task, adopt the serial queue else take concurrent queue.

And lastly this is a way of penetrating back to the main thread when we are done with our business :

DispatchQueue.main.async {
     // Do something here
}
rd_
  • 395
  • 3
  • 13