GCD: Using Dispatch Groups for Fun and Profit

I’m republishing this article here so I can reference it later.

I recently needed a way to perform an unknown number of asynchronous http requests and wait until they were all done before proceeding. dispatch_groups are a neat feature of Grand Central Dispatch (GCD) that made this easy to do.

There are a couple of ways to use dispatch_groups but the basic idea is the same: create a dispatch_group, give it some tasks, and wait for it to finish those tasks.

First, let’s create a group:

dispatch_group_t group = dispatch_group_create();

There are two ways to add tasks to our dispatch_group. You can either call dispatch_group_async with the group, a dispatch_queue and block to run or manually call dispatch_group_enter.

Calling dispatch_group_async looks something like this:

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Do stuff on a global background queue here
});

Or you can manage tasks manually by calling dispatch_group_enter and dispatch_group_leave in pairs:

dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Do stuff on a global background queue here
    dispatch_group_leave(group);
});

Using dispatch_group_enter/dispatch_group_leave is handy when you’re using libraries that provide asynchronous operation (e.g. AFNetworking).

The last thing to do is wait for all of the tasks to finish. The preferred way to do this is to use dispatch_group_notify:

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // Do stuff after all tasks are finished
});

dispatch_group_notify is nice because it does not block the thread it’s called from. If you have a reason to block the current thread, use dispatch_group_wait instead:

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// Do stuff after all tasks are finished

A full example looks like this:

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Do stuff on a global background queue here
});

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Do more stuff on a global background queue here
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // Do stuff after all tasks are finished
});

One important note about dispatch objects: If your project’s deployment target is less than 6.0, you need to manage memory with dispatch_retain and dispatch_release for any dispatch objects you create (groups, queues, semaphores). ARC takes care of managing memory for dispatch objects when your deployment target is 6.0+ (10.8+ for OS X).

 
55
Kudos
 
55
Kudos

Now read this

More on Lazy Initialization in Swift

@ColinEberhardt wrote a great post titled Swift Initialization and the Pain of Optionals. In the post, Colin mentions not being able to pass parameters to initializers when assigning to a variable marked lazy. The workaround he found was... Continue →