iOS GCD: Difference between any global queue and the one with background priority (DISPATCH_QUEUE_PRIORITY_BACKGROUND)?

Nirav Bhatt picture Nirav Bhatt · Jul 31, 2014 · Viewed 14.6k times · Source

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.

Answer

quellish picture quellish · Jul 31, 2014

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.