You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It is used to manage the execution of multiple tasks concurrently, can be used in a situation where you need to aggregate the results of these tasks before proceeding further.
DispatchGroup is not the best choice when one task is dependent on other.
Initialization:
letgroup=DispatchGroup()
Entering the Group:
You need to call group.enter() before starting an asynchronous task.
group.enter()DispatchQueue.global().async{// Perform your async task here
group.leave()// Call leave() when the task is completed}
Waiting for All Tasks to Complete:
There are two ways to wait for the tasks to complete:
Synchronous Wait:
group.wait()// This blocks the current thread until all tasks in the group have completed
Asynchronous Wait:
group.notify(queue:.main){// This block is executed when all tasks in the group have completed.print("All tasks are completed")}
DispatchQueue is an object that manages the execution of tasks serially or concurrently on main thread or on a background thread.
Serial Custom DispatchQueue
A custom DispatchQueue ensures that tasks are executed one at a time in the order they are added.
letqueue=DispatchQueue(label:"com.avii.example")
Using sync on Custom Serial Queue
Defination: When using sync, the calling thread blocked until the submitted block completes.
Purpose: Ensures tasks are performed sequentially and allows for thread-safe access to shared resources.
Below we created an example, that demonstrate, how sync is useful to prevent race conditions.
classThreadSafeExample{private(set)varbalance=100privateletqueue=DispatchQueue(label:"com.avii.example")func decrement(by value:Int)->String{
queue.sync{
if balance >= value {Thread.sleep(forTimeInterval:4)
balance -= value
return"Success"}return"Insufficient Balance"}}func get()->Int{
balance
}}letobj=ThreadSafeExample()DispatchQueue.global().async{print(obj.decrement(by:100))print(obj.balance)}DispatchQueue.global().async{print(obj.decrement(by:50))print(obj.balance)}
As mentioned above, DispatchQueue initializer creates a serial queue.
letqueue=DispatchQueue(label:"com.avii.example")
Then keep in mind that execution of multiple DispatchWorkItems will happen sequentially.
Like, in the below example, we are printing numbers inside DispatchWorkItem block in the duration of 1 second and also scheduling a block execution after 3 seconds to cancel our other block.
letworkItem=DispatchWorkItem{
for i in 1...10{print(i, workItem.isCancelled)Thread.sleep(forTimeInterval:1)}}letqueue=DispatchQueue(label:"com.avii.testing1")
queue.async(execute: workItem)
queue.asyncAfter(deadline:.now()+.seconds(3)){print("I'm Cancelling now")
workItem.cancel()}
What problem Concurrent DispatchQueue Barrier flag solves ?
Problem:
Suppose there is a resource that is shared between mutiple threads. One thread is performing write operation (which is time consuming) and can take few seconds to complete. At the same time if one thread tried to access that resource, it will get unpredictable reesult (like mismatch between number of items).
To solve this problem you may think to use serial queue, in which you will wrap your read operations into queue's sync/async blocks and will wait until the write operations completes before executing read operations.
Let's proceed with an example:
finalclassMessenger{staticletshared=Messenger()privateinit(){}privatevararray=[String]()// Creating a serial queueprivateletqueue=DispatchQueue(label:"com.avii.mt")// Simulating the posting of a message using Thread.sleep.func postMessage(_ message:String){
queue.async{[weak self]inThread.sleep(forTimeInterval:4)self?.array.append(message)}}func getLastMessage(_ completion:@escaping((String?)->Void)){
queue.async{[weak self]incompletion(self?.array.last)}}}// Appending operation using #Thread 1DispatchQueue.global().async{Messenger.shared.postMessage("M1")}// Reading operation using #Thread 2DispatchQueue.global().async{Messenger.shared.getLastMessage{ message inprint(message ??"nil")}}
In the above example, we used used serial queue.
But this approach will slow down my read operations. Now my read operations will execute sequentially, with one read operation having to wait for another to complete.
In that case, barrier flag can help us to solve this problem.
Using this flag, we can prevent the read operations from happening until the write operation has completed.
Barrier flag Definition form Apple:
When submitted to a concurrent queue, a work item with this flag acts as a barrier. Work items submitted prior to the barrier execute to completion, at which point the barrier work item executes. Once the barrier work item finishes, the queue returns to scheduling work items that were submitted after the barrier.