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 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
}
  1. all task which were started are finished
  2. Single Barrier task
  3. 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]