Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding of dispatch_sync and global queues

I'm studying GCD mechanism and I have couple of questions. I would appreciate if you correct me, if I get material wrong.

1. question) As far as i know, GCD have 4 global concurrent queues with different priority. For example, when we write DISPATCH_QUEUE_PRIORITY_HIGH, we get one of this queues. That queues are not empty, some Apple system processes running on them. So, when we add block of code in some of that queue, it could be, for example, n number task in a row, when n is random integer number.

Now, when we add block of code, like

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
      // Heavy calculations
});

in viewDidLoad, all of UI components will be blocked, until:

  1. Apple system tasks will be completed (because we add our task last in that queue, and should wait until other system tasks finish)
  2. Until our code will complete.

Am I right? I know, we should use dispatch_async here, I just wonder how things work.

2. question) As far as i know, all of global queues are concurrent queues, which mean, that it could manage tasks either through context switch or parallelism. However, when we get to that queue through dispatch_sync, we forced to wait, when all of work will be done. The only thing that is different from serial queues in that case, is order of operations. For example, if serial queue have task 1, task 2, task 3 and task 4, it will do this strictly in order, but concurrent queue could change it order, to complete light-weight operations first.

So, my questions is, why should we ever do dispatch_sync? In my understanding, main thread will be blocked until dispach_sync code block will finish.

like image 496
Evgeniy Kleban Avatar asked Jul 11 '15 20:07

Evgeniy Kleban


People also ask

What is DispatchQueue global?

An object that manages the execution of tasks serially or concurrently on your app's main thread or on a background thread. iOS 8.0+ iPadOS 8.0+ macOS 10.10+ Mac Catalyst 13.0+ tvOS 9.0+ watchOS 2.0+ Xcode 8.0+

What is a serial queue?

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue.

What is serial and concurrent queue?

CONCURRENT QUEUES (often known as global dispatch queues) can execute tasks simultaneously; the tasks are, however, guaranteed to initiate in the order that they were added to that specific queue, but unlike serial queues, the queue does not wait for the first task to finish before starting the second task.

What is Dispatch_async?

Submits a block for asynchronous execution on a dispatch queue and returns immediately.


1 Answers

GCD have 4 global concurrent queues with different priority. For example, when we write DISPATCH_QUEUE_PRIORITY_HIGH, we get one of this queues. That queues are not empty, some Apple system processes running on them.

At any given time, the queues may be empty or may not. There's no way to know. Yes, the frameworks may add things to those queues just like your code does.

The queue does not run things, though. The queue is a data structure. It holds tasks in order. GCD manages a set of worker threads, creating new ones or having them exit, as necessary. Those worker threads take tasks off of the queues and executes them.

when we add block of code, like

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

      // Heavy calculations

  });

in viewDidLoad, all of UI components will be blocked, until: 1 - Apple system tasks will be completed (because we add our task last in that queue, and should wait until other system tasks finish) 2 - Until our code will complete.

As its name implies, dispatch_sync() is synchronous. That means it doesn't return until it has completed the work it was asked to do (the block that you passed). Whether you have to wait for any other tasks on the queue or not depends on available system resources. The queue is concurrent, as you noted, so tasks can be pulled off it to run concurrently. If there are enough free CPU cores, GCD may start enough worker threads to run all of the tasks on the queue simultaneously. So, your task does not have to wait for other tasks to complete, it just has to wait for those tasks to have been started (popped off of the head of the queue) and for there to be a spare worker thread available.

You would only have to wait for other tasks to complete if all system resources (like CPU cores) are busy.

As far as i know, all of global queues are concurrent queues, which mean, that it could manage tasks either through context switch or parallelism. However, when we get to that queue through dispatch_sync, we forced to wait, when all of work will be done.

No, that's wrong, as I explained above. The only thing that you know has to be completed before dispatch_sync() returns is the one task you submitted using it. It does not have to wait for any other tasks on that queue, unless all CPU cores are busy.

The only thing that is different from serial queues in that case, is order of operations. For example, if serial queue have task 1, task 2, task 3 and task 4, it will do this strictly in order, but concurrent queue could change it order, to complete light-weight operations first.

No. Concurrent queues start operations strictly in order, just like serial queues. It's just that a serial queue won't start another operation until the current one, if any, completes. A global concurrent queue will allow all of its operations to start and run simultaneously, up to the available resources. The queues have no way of knowing if an operation is lightweight.

So, my questions is, why should we ever do dispatch_sync? In my understanding, main thread will be blocked until dispach_sync code block will finish.

Concurrency and synchronous behavior are two separate concepts. Synchronous vs. asynchronous determines the behavior of the caller. It determines if the caller is allowed to proceed before the work is done.

Concurrent vs. serial determines how the tasks which were submitted run. A concurrent queue allows the tasks to run concurrently with one another. A serial queue only allows one of its tasks to run at a time.

It can make sense to call dispatch_sync() from the main thread, but you have to be careful. It can be necessary, for example, when using a serial queue to synchronize access to a data structure which is shared by multiple threads. The general rule is that you need to avoid blocking the main thread for long periods. It's OK to block it if you have good reason to believe that it will be for very short periods that the user won't be able to perceive.

You definitely don't want to use dispatch_sync() from the main thread for "Heavy calculations", as you put it.

In general, you use dispatch_sync() when you need the task to be complete before you can go on. Often, you can restructure your code to instead use dispatch_async() and put the subsequent code into the task as a continuation step (or completion handler). But you can't always do that.

like image 51
Ken Thomases Avatar answered Sep 25 '22 23:09

Ken Thomases