Grand Central Dispatch

  • GCD manages threads according to the available system resources.
  • Under the hood it manages a shared thread pool
  • You add blocks of code to DispatchQueue and GCD decides which thread to execute them on.
  • Dispatch queues are thread-safe which means that you can access them from multiple threads simultaneously.
  • GCD alleviates the problem of too many threads being created
  • You think about work items in a queue instead of threads.

Serial vs Concurrent Queue

Serial queues guarantee that only one task runs at any given time.

Concurrent queues allow multiple tasks to run at the same time.

  • Tasks are guaranteed to start in the order they were added.
  • Tasks can finish in any order and you have no knowledge of the time it will take for the next task to start, nor the number of tasks that are running at any given time.

GCD provides three main types of queues

  • Main queue: Main Thread, Serial Queue
  • Global queues: Concurrent queues
  • There are four Global queues with different priorities: high, default, low, and background.
  • Custom queues: queues that you create which can be serial or concurrent. These actually trickle down into being handled by one of the global queues.

Note: You must always access UIKit classes on the main thread!

QoS

When setting up the global concurrent queues, you don’t specify the priority directly. Instead you specify a Quality of Service (QoS) class property.

  • User-interactive
  • User-initiated
  • Utility
  • Default
  • Background

Synchronous vs. Asynchronous

With GCD, you can dispatch a task either synchronously or asynchronously.

  • A synchronous function returns control to the caller after the task is completed.
  • An asynchronous function returns immediately, ordering the task to be done but not waiting for it.
  • Thus, an asynchronous function does not block the current thread of execution from proceeding on to the next function.
DispatchQueue.global(qos: .userInitiated).async { 
  let overlayImage = self.faceOverlayImageFromImage(self.image)
  DispatchQueue.main.async { 
     // Update UI
  }
}

Use async in different queue

  • Main Queue: Update the UI after completing work in a task on a concurrent queue.
  • Calling async on main queue guarantees that this new task will execute sometime after the current method finishes.

  • Global Queue: This is a common choice to perform non-UI work in the background.

  • Custom Serial Queue: A good choice when you want to perform background work serially and track it. This eliminates resource contention since you know only one task at a time is executing.

When and where to use sync

  • Main Queue: Be careful using sync on main queue.
  • Global Queue: This is a good candidate to sync work through dispatch barriers or when waiting for a task to.
  • Custom Serial Queue: Be VERY careful in this situation; if you’re running in a queue and call sync targeting the same queue, you’ll definitely create a deadlock.
  • If you call sync and target the current queue you’re already running on. This will result in a deadlock situation.

Deadlock in GCD

dispatch_queue_t queue = dispatch_queue_create("my.label", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    dispatch_sync(queue, ^{
        // outer block is waiting for this inner block to complete,
        // inner block won't start before outer block finishes
        // => deadlock
    });

    // this will never be reached
});

DispatchWorkItems

The DispatchWorkItem class is an encapsulation of the concept of work item. In order to use it, we initialize it an instance using a block and then we can either run it on the current thread or submit it to a dispatch queue.

A dispatch work item has a cancel flag. If it is cancelled before running, the dispatch queue won’t execute it and will skip it. If it is cancelled during its execution, the cancel property return True. In that case, we can abort the execution.

//create the dispatch work item
var workItem = DispatchWorkItem {
    for i in 1...5 {
        if (workItem.isCancelled)!{
            break
        }
    }
}

//submit the work item to the default global queue
DispatchQueue.global().async(execute: workItem)

//cancelling the task after 3 seconds
DispatchQueue.global().async {
    sleep(3)
    workItem?.cancel()
}

results matching ""

    No results matching ""