37
votes

I am reading Concurrency Programming Guide and things confuse me.

I see a lot of code invoking the following for any background task:

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

Now what I mean by 'background' is the popular meaning:

Something that gets executed anywhere other than the main (UI) thread

So following the docs, the above statement returns any non-main-thread queue with differing priorities.

My question is - why does then DISPATCH_QUEUE_PRIORITY_BACKGROUND exist? Lately I also see many async tasks using DISPATCH_QUEUE_PRIORITY_BACKGROUND specifically to perform background tasks.

Aren't queues returned with DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_PRIORITY_LOW or DISPATCH_QUEUE_PRIORITY_HIGH run very much away from main thread, if they are being returned using dispatch_get_global_queue?

Aren't they background queues? What specific purpose does a queue returned with DISPATCH_QUEUE_PRIORITY_BACKGROUND serve? I already referred to this but it does not clarify much, other than the popular meaning I mentioned above.

I am sure I am pretty confused with words - background and background queues. If someone can explain (better, graphically) - will be a great help.

2
I might also refer you to WWDC 2014 video - Power, Performance and Diagnostics: What's new in GCD and XPC which walks through the new QoS framework (iOS 8, Mac OS X 10.10) for queue priorities, including business rationale for picking one QoS level over another. That includes a mapping to the old dispatch queue priorities, which might be helpful for selecting the appropriate queue priority.Rob
+1 thanks, I was searching for something alike, but wasn't sure where to search. Will check it out.Nirav Bhatt
Also keep in mind that QoS is a separate concept from the priority bands of the global concurrent queue - even though on their surface they look similar, the implementation is different and the semantics are different.quellish
no, the old priority bands are mapped directly to QoS classes and are not a separate concept and have no different semantics, see the WWDC session referenced abovedas
sure, QoS is a broader concept that the old global queue priorities, but where the concepts overlap they are identical in effect, and the implementation in the overlapping areas is identical (it's a direct mapping of old -> new).das

2 Answers

37
votes

This is explained pretty well in the dispatch/queue.h header:

DISPATCH_QUEUE_PRIORITY_HIGH Items dispatched to the queue will run at high priority, i.e. the queue will be scheduled for execution before any default priority or low priority queue.

DISPATCH_QUEUE_PRIORITY_DEFAULT Items dispatched to the queue will run at the default priority, i.e. the queue will be scheduled for execution after all high priority queues have been scheduled, but before any low priority queues have been scheduled.

DISPATCH_QUEUE_PRIORITY_LOW Items dispatched to the queue will run at low priority, i.e. the queue will be scheduled for execution after all default priority and high priority queues have been scheduled.

DISPATCH_QUEUE_PRIORITY_BACKGROUND Items dispatched to the queue will run at background priority, i.e. the queue will be scheduled for execution after all higher priority queues have been scheduled and the system will run items on this queue on a thread with background status as per setpriority(2) (i.e. disk I/O is throttled and the thread's scheduling priority is set to lowest value).

And keep in mind this is a global queue. Other things, like system frameworks, may be scheduling in to it. It's very easy to starve the priority bands - if there are a lot of DISPATCH_QUEUE_PRIORITY_HIGH tasks being scheduled, tasks at the default priority may have to wait quite a while before executing. And tasks in DISPATCH_QUEUE_PRIORITY_BACKGROUND may have to wait a very long time, as all other priorities above them must be empty.

A lot of developers abuse the global concurrent queue. They want a execute a block, need a queue, and just use that at the default priority. That kind of practice can lead to some very difficult to troubleshoot bugs. The global concurrent queue is a shared resource and should be treated with care. In most cases it makes more sense to create a private queue.

A concurrent queue is not asynchronous, it is concurrent. Synchronous tasks can still be scheduled into it, and they will still execute synchronously. Concurrent queues, like serial queues, dequeue in FIFO order. They execute blocks concurrently, unlike serial queues. Concurrent and asynchronous are not the same thing.

Also keep in mind that if the main thread is idle, a concurrent queue can re-use that thread - and in fact will prefer doing that to creating new threads. Using a concurrent queue will not guarantee you will not block the user interface:

Blocks submitted to these dispatch queues are invoked on a pool of threads fully managed by the system. No guarantee is made regarding which thread a block will be invoked on; however, it is guaranteed that only one block submitted to the FIFO dispatch queue will be invoked at a time.

GCD makes no guarantees about what thread will be used to execute a block on a concurrent queue. If you use the main queue, the block will be executed serially on the main thread. A concurrent queue can use any thread, and as an optimization will prefer to use existing threads. It will only create a new thread if no threads are available to be reused. And in fact the main thread is often it's first choice (if the main thread is available for work) because it is "warm".

To reiterate: With Grand Central Dispatch you can be certain that a task will execute on the main thread (by submitting to the main queue). You cannot be certain that a task will not execute on the main thread.

14
votes

If you have many background tasks, the CPU or CPUs of your device will be shared between all of them. Most of the time that is the right thing to do. If a task takes too long to finish, you solve the problem by trying to make it more efficient.

In very rare cases, you may have a task that takes a long time, but it is Ok to wait for it. So you give it BACKGROUND priority. If there is any work to do at NORMAL priority, that work will be done first, and only when there is a spare CPU doing nothing else, the BACKGROUND task will be performed. And there is the queue with HIGH priority; tasks in that queue will be executed first; you would do that if one specific task needs to be finished as quick as possible even if it means that other tasks are delayed.

From a point of view of your programming logic, all three queues are identical. It just affects which tasks the OS tries to finish first, and which it doesn't care about that much.