important async Threading why: understand DispatchGroup is

Subscribe to my newsletter and never miss my upcoming articles

Hello everyone, Leo here.

Today we'll see a very common problem in computer science, and yes is about asynchronous programming. There's a lot to explore inside the apple's GCD framework but today we'll dive into a class called DispatchGroup and analyse what it can do for us.

No more talking(writing?), let's code.

The problem:

You have a various asynchronous calls and you want to have a single completion handler when all calls are done. Imagine that you have to download various resources and only after all have finished you could enable a button to continue the app flow.

This task would be a little complicated if you have to code yourself this behaviour, luck us that we have the DispatchGroup class to achieve that!

From the DispatchGroup definition docs :

Groups allow you to aggregate a set of tasks and synchronize behaviors on the group. You attach multiple work items to a group and schedule them for asynchronous execution on the same queue or different queues. When all work items finish executing, the group executes its completion handler. You can also wait synchronously for all tasks in the group to finish executing.

Let's examine the code below:

let queue = DispatchQueue.global() //1
let dispatchGroup = DispatchGroup() //1

dispatchGroup.enter() //2
queue.async(group: dispatchGroup) {
    sleep(3) // simulate a heavy backgroud task
    print("heavy async task 1 - done")
    dispatchGroup.leave() //2
}

dispatchGroup.enter() //2
queue.async(group: dispatchGroup) {
    sleep(1)
    print("heavy async task 2 - done")
    dispatchGroup.leave() //2
}

dispatchGroup.notify(queue: queue) { //3
    print("now we can handle when everything is done, ex:  setup a button animation to appear")
}

print("All work started!")

So we're looking at:

  1. First we have to create the variables queue and the dispatchGroup
  2. For every heavy task we need to call the dispatchGroup.enter() and when the job is finished don't forget to call the dispatchGroup.leave()
  3. When all the groups are finished we run the dispatchGroup.notify(queue: queue), this enables handling all the different async threads with one completion handler

This should appear on your console log:

Screen Shot 2020-07-29 at 08.54.47.png

I recommend you to run this code in playground to understand better.

And you can use DispatchGroup inside a for loop too:

let queue = DispatchQueue.global()
let dispatchGroup = DispatchGroup()

for num in 0...2 {
    dispatchGroup.enter()
    queue.async(group: dispatchGroup) {
        sleep(UInt32(Int.random(in: 1...3)))
        print("download archive \(num) - done")
        dispatchGroup.leave()
    }
}

dispatchGroup.notify(queue: queue) {
    print("now we can handle when everything is done, ex:  setup a button animation to appear")
}

print("All work started!")

So today we learn about how to do a completion handler to many async calls. Later articles about threading we'll explore semaphores and how they can be very useful to handle shared resource inside your app, so don't miss it!

Feedbacks are welcome and please comment if you want to add something.

Thanks for the reading and... That's all folks!

Comments (3)

Bolaji Ayodeji's photo

Thanks for sharing!

Leonardo Maia Pugliese's photo

Thanks for reading man!

qison meri's photo

To thank you for your feedback, you will be given a Peter Piper Pizza Coupon Code to redeem free Pizza.

guestsurvey.onl/peter-piper-pizza-survey