main.async vs main.sync() vs global().async in Swift3 GCD
In first case, you run the code on main
and then you use main.sync
on the main thread. In essence, you are trying to tell the main
queue to wait for itself - which is obviously nonsense and therefore it causes crash.
In the second case, you run the code on the background thread, and then you use main.sync
to wait until the main
thread can run the block provided in main.sync
.
In general, I would use async
and not sync
all the time, unless sync
is necessary - and always sync one thread (DispatchQueue
) with a different one, never with the same one.
In simple term i come to conclusion that -
- Queue- There are 3 Types of Queue i.e. 1 Main Queue, 4 Global Queue and Any No. of Custom Queues.
- Threads- One is Main Thread and other background threads which system provides to us.
DispatchQueue.main.async
-It means performing task in main queue with using of background thread (w/o blocking of UI) and when task finish it automatic Updated to UI because its already in Main Queue.
DispatchQueue.global().async along with global().sync
It means performing task in Global Queue with using of background thread and when task finish, than global().sync use bring the work from globalQueue to mainQueue which update to UI.
Reason of My App Crash
I was trying to bring the completed task to MainQueue by using(main.sync), but it was already on MainQueue because i hadnt switched the Queue, and this create DeadLock (MainQueue waiting for itself), causes my app crash
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 systemprivate 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 calculationsuserInitiated
- high priority queue - relevant for UI. Up to several seconds. For example loading data for showing on UIdefault
- default priority queueutility
- 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 insideQueue
orDispatchGroup
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 0notify
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 norace 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]