GCD
Thread -> GCD -> Operation + OperationQueue(life cycle, dependencies between different queues, cancel)
Grand Central Dispatch GCD libdispatch operates on dispatch queues DispatchQueue with a FIFO order
DispatchQueue.<queue>.<sync/async> means run a <sync/async> task on the <queue>
GCD supports:
global queue - shared between whole iOS operation system
private queue
main - global queue, serial queue on a main thread which is used to working with UI
DispatchQueue.main
global() - global queue, concurrent queue.
DispatchQueue.global()
DispatchQueue.global(qos: .background)
Custom queue: - private queue, serial or concurrent custom queue
DispatchQueue(label: "serialQueue") // without attributes
DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
QUEUE
Usually when we talk about concurrent we talk about queues. Count of threads are depends on OS conditions. There are no run loop[About] for worker thread.
concurrent has different groups of queues with priorities(main thread, hight, default, low, background) which you pass at the task using qos QoSClass(Quality of Service) (priority GlobalQueuePriority is deprecated):
userInteractive - main thread - the most priority. It can be used for very fast calculation which immediately are reflected on UI. For example animation calculations
userInitiated - high priority queue - relevant for UI. Up to several seconds. For example loading data for showing on UI
default - default priority queue
utility - low priority queue - up to several minutes like working with big data like images, processing...
background - background priority queue - up to several hours while app is on background like sync data.
sync/async
sync - block a current thread and wait when it will be finished on a specified queue
async - do not block a current thread and send an execution block of code to the specificified queue
Common mistake: deadlock
If you call DispatchQueue.main.sync on a main thread - the app will be frozen because the calling DispatchQueue.main.sync starts waiting immediately when the dispatched block is finished (dispatched block is not started)
Some notes:
DispatchWorkItem - delaying/cancelling/prioritise a task inside Queue or DispatchGroup
DispatchGroup if you are going to execute several async tasks with a single callback even on different queues. All these task should be grouped. DispatchGroup contains thread safe counter and when it equals 0 notify is called
//create group
let group = DispatchGroup()
//case 1
DispatchQueue.<queue>.async(group: group) //
//case 2 - manual
group.enter() //<- +1
DispatchQueue.global().async {
//logic
group.leave() //<- -1
}
//notification
group.notify(queue: <callback_queue>) {
//logic
}
Barrier flag inside concurrent queue for sync/async task guaranties that there is no race condition(several threads simultaneously make write operation). The best place for it is custom queue because does not block any others global tasks:
customQueue.async(flags: .barrier) {
//logic
someProperty = someValue
}
- all task which were started are finished
- Single Barrier task
- Executing all other tasks in the queue
thread safe operation can be reached through Barrier in concurrent queue for shared variable:
- read - sync operation on concurrent queue
- write - async operation with
barrier
[Thread safe singleton]
[Sync vs Async]
[iOS Synchronization]