ChatGPT解决这个技术问题 Extra ChatGPT

Difference between dispatch_async and dispatch_sync on serial queue?

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?


B
Bryan Chen

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


it can also print 2134 and 1243
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 for second example? because some other thread may running dispatch_sync(_serialQueue, ^{ /*change shared data*/ }); at same time.
@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.
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.
J
JRG-Developer

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.


I think your get wrong order. first example is async which is the non-blocking version
I've edited your answer to what I think you meant. If this is not the case, please change it and clarify.
What if you call dispatch_sync and then dispatch_async on the same queue? (and vice versa)
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.
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.
r
rd_

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
}