What is the difference between DispatchQueue.global().sync vs main thread

596 views Asked by At

what is the difference between below 2 piece of code while output is same for both

print("1")
print("k")
print("2")
print("3")
print("4")
print("1")
DispatchQueue.global().sync {
    print("k")
}
print("2")
print("3")
print("4")

I want to understand how sync works with global() queues.

3

There are 3 answers

0
Sudeep P H On BEST ANSWER

The output may appear the same, but there is actually a difference in how the code is executed.In the first code snippet, the print statements are executed sequentially, one after another, in the order they are written.

In the second code snippet The statement print("1") is executed first and prints "1" to the console.The DispatchQueue.global().sync block is encountered. The code inside this block will be executed synchronously on a global queue, which means it will run on a separate thread but will block the current thread until it completes.Inside the DispatchQueue.global().sync block, print("k") is executed, printing "k" to the console.After the sync block completes, the execution continues to the next line, which is print("2"). It prints "2" to the console. Similarly, print("3") and print("4") are executed next, printing "3" and "4" respectively.

So the output for the both code snippet will be

1
k
2
3
4
0
Coder ACJHP On

First of all we need to understand what is DispatchQueue. Briefly DispatchQueue is An object that manages the execution of tasks serially or concurrently on our app's main thread or on a background thread (for more). We can create dispatchQueue associated with the main (MAIN) thread or getting the global (GLOBAL) system queue with the specified quality-of-service class. Briefly that's mean both of them (MAIN & GLOBAL) are dispatchQueue.

Differences:
Main: automatically created by system, it associates it with our application’s main thread and starts our app with it.
Global: is a system queue with the specified quality-of-service class like userInteractive, utility, background etc.

Use cases:
Main: mostly used for UI tasks and attempting to synchronously execute a work item on the main queue results in deadlock.
Global: mostly long running task, such as making network call
In this case both of codes will print same output because you calling global dispatch with sync func not async thats mean thread will wait until your case finishes then moves to next line, if you use async result will be different like here

print(1)
DispatchQueue.main.async {
    print(2)
}
print(3)

//prints: 1,3,2
0
Rob On

Consider your second example:

print("1")
DispatchQueue.global().sync {
    print("k")
}
print("2")
print("3")
print("4")

The sync call blocks the current thread and dispatches the print("k") to the default global queue, and when it finishes, it will resume execution at the print("2") line. Ostensibly, this means that the print("k") would run on a worker thread pulled from the default global queue’s thread pool and that the current thread will be blocked until that finishes. You will see it print “1”, “k”, “2”, “3”, and “4”, in that order.

In reality, though, because it was dispatched with sync, the print("k") will actually run on the current thread. It might seem confusing at first glance, but it does not run on a thread from the global queue’s thread pool. As the sync documentation says:

As a performance optimization, this function executes blocks on the current thread whenever possible…

It does this because there is a cost associated with switching from one thread to another. Given that sync would block the current thread anyway, GCD does this clever little optimization whereby it runs the code synchronously dispatched to the global queue on the current thread, thereby avoiding the costly context switch where possible. There are exceptions to this optimization (e.g., notably when dispatching to the main queue, which is irrelevant to the above code snippet), but generally sync to the global queue just runs it synchronously on the current thread.

So, even without this optimization, both of your examples will print the output of “1”, “k”, “2”, “3”, and “4”. But, because of this clever optimization, your two code snippets are actually far more similar than one might have otherwise suspected. Effectively, they both just run these five print statements, in succession, on the current thread.


You said that you want to understand how sync works with global queues. The reality is that you practically never dispatch with sync to a global queue. The primary purpose of the global queue is to get something slow and synchronous off the current thread, e.g., to avoid blocking the main thread. So one would practically never use sync to run something on a global queue, because you would end up just blocking the thread that you wanted to avoid blocking.

If you are wondering where you might actually use sync, a common use-case is for synchronizing access to some shared resource. Perhaps you have some state variable that you access from different threads, you might use sync to synchronize your access to that. But you would never use a global queue for that (since it is concurrent and doesn't support barriers, it is of no use for synchronization). We would use a private queue for synchronization (or locks or actors, but not global queues). This is just one example, but, in short, there are special use-cases for sync to private dispatch queues, but rarely with global queues.

In short, only use sync if it is truly essential to block the current thread while the other queue does its work. Otherwise, it is prudent to use async. If you find yourself reaching for sync, always ask yourself whether you really need to block the current thread while the dispatched code runs.