Waiting until two async blocks are executed before starting another block Waiting until two async blocks are executed before starting another block ios ios

Waiting until two async blocks are executed before starting another block


Use dispatch groups: see here for an example, "Waiting on Groups of Queued Tasks" in the "Dispatch Queues" chapter of Apple's iOS Developer Library's Concurrency Programming Guide

Your example could look something like this:

dispatch_group_t group = dispatch_group_create();dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {    // block1    NSLog(@"Block1");    [NSThread sleepForTimeInterval:5.0];    NSLog(@"Block1 End");});dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {    // block2    NSLog(@"Block2");    [NSThread sleepForTimeInterval:8.0];    NSLog(@"Block2 End");});dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {    // block3    NSLog(@"Block3");});// only for non-ARC projects, handled automatically in ARC-enabled projects.dispatch_release(group);

and could produce output like this:

2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block12012-08-11 16:10:18.052 Dispatch[11858:1d03] Block22012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3


Expanding on Jörn Eyrich answer (upvote his answer if you upvote this one), if you do not have control over the dispatch_async calls for your blocks, as might be the case for async completion blocks, you can use the GCD groups using dispatch_group_enter and dispatch_group_leave directly.

In this example, we're pretending computeInBackground is something we cannot change (imagine it is a delegate callback, NSURLConnection completionHandler, or whatever), and thus we don't have access to the dispatch calls.

// create a groupdispatch_group_t group = dispatch_group_create();// pair a dispatch_group_enter for each dispatch_group_leavedispatch_group_enter(group);     // pair 1 enter[self computeInBackground:1 completion:^{    NSLog(@"1 done");    dispatch_group_leave(group); // pair 1 leave}];// again... (and again...)dispatch_group_enter(group);     // pair 2 enter[self computeInBackground:2 completion:^{    NSLog(@"2 done");    dispatch_group_leave(group); // pair 2 leave}];// Next, setup the code to execute after all the paired enter/leave calls.//// Option 1: Get a notification on a block that will be scheduled on the specified queue:dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{    NSLog(@"finally!");});// Option 2: Block an wait for the calls to complete in code already running// (as cbartel points out, be careful with running this on the main/UI queue!)://// dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // blocks current thread// NSLog(@"finally!");

In this example, computeInBackground:completion: is implemented as:

- (void)computeInBackground:(int)no completion:(void (^)(void))block {    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{        NSLog(@"%d starting", no);        sleep(no*2);        block();    });}

Output (with timestamps from a run):

12:57:02.574  2 starting12:57:02.574  1 starting12:57:04.590  1 done12:57:06.590  2 done12:57:06.591  finally!


With Swift 5.1, Grand Central Dispatch offers many ways to solve your problem. According to your needs, you may choose one of the seven patterns shown in the following Playground snippets.


#1. Using DispatchGroup, DispatchGroup's notify(qos:flags:queue:execute:) and DispatchQueue's async(group:qos:flags:execute:)

The Apple Developer Concurrency Programming Guide states about DispatchGroup:

Dispatch groups are a way to block a thread until one or more tasks finish executing. You can use this behavior in places where you cannot make progress until all of the specified tasks are complete. For example, after dispatching several tasks to compute some data, you might use a group to wait on those tasks and then process the results when they are done.

import Foundationimport PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution = truelet queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)let group = DispatchGroup()queue.async(group: group) {    print("#1 started")    Thread.sleep(forTimeInterval: 5)    print("#1 finished")}queue.async(group: group) {    print("#2 started")    Thread.sleep(forTimeInterval: 2)    print("#2 finished")}group.notify(queue: queue) {    print("#3 finished")}/* prints: #1 started #2 started #2 finished #1 finished #3 finished */

#2. Using DispatchGroup, DispatchGroup's wait(), DispatchGroup's enter() and DispatchGroup's leave()

import Foundationimport PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution = truelet queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)let group = DispatchGroup()group.enter()queue.async {    print("#1 started")    Thread.sleep(forTimeInterval: 5)    print("#1 finished")    group.leave()}group.enter()queue.async {    print("#2 started")    Thread.sleep(forTimeInterval: 2)    print("#2 finished")    group.leave()}queue.async {    group.wait()    print("#3 finished")}/* prints: #1 started #2 started #2 finished #1 finished #3 finished */

Note that you can also mix DispatchGroup wait() with DispatchQueue async(group:qos:flags:execute:) or mix DispatchGroup enter() and DispatchGroup leave() with DispatchGroup notify(qos:flags:queue:execute:).


#3. Using Dispatch​Work​Item​Flags barrier and DispatchQueue's async(group:qos:flags:execute:)

Grand Central Dispatch Tutorial for Swift 4: Part 1/2 article from Raywenderlich.com gives a definition for barriers:

Dispatch barriers are a group of functions acting as a serial-style bottleneck when working with concurrent queues. When you submit a DispatchWorkItem to a dispatch queue you can set flags to indicate that it should be the only item executed on the specified queue for that particular time. This means that all items submitted to the queue prior to the dispatch barrier must complete before the DispatchWorkItem will execute.

Usage:

import Foundationimport PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution = truelet queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)queue.async {    print("#1 started")    Thread.sleep(forTimeInterval: 5)    print("#1 finished")}queue.async {    print("#2 started")    Thread.sleep(forTimeInterval: 2)    print("#2 finished")}queue.async(flags: .barrier) {    print("#3 finished")}/* prints: #1 started #2 started #2 finished #1 finished #3 finished */

#4. Using DispatchWorkItem, Dispatch​Work​Item​Flags's barrier and DispatchQueue's async(execute:)

import Foundationimport PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution = truelet queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)queue.async {    print("#1 started")    Thread.sleep(forTimeInterval: 5)    print("#1 finished")}queue.async {    print("#2 started")    Thread.sleep(forTimeInterval: 2)    print("#2 finished")}let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .barrier) {    print("#3 finished")}queue.async(execute: dispatchWorkItem)/* prints: #1 started #2 started #2 finished #1 finished #3 finished */

#5. Using DispatchSemaphore, DispatchSemaphore's wait() and DispatchSemaphore's signal()

Soroush Khanlou wrote the following lines in The GCD Handbook blog post:

Using a semaphore, we can block a thread for an arbitrary amount of time, until a signal from another thread is sent. Semaphores, like the rest of GCD, are thread-safe, and they can be triggered from anywhere. Semaphores can be used when there’s an asynchronous API that you need to make synchronous, but you can’t modify it.

Apple Developer API Reference also gives the following discussion for DispatchSemaphore init(value:​) initializer:

Passing zero for the value is useful for when two threads need to reconcile the completion of a particular event. Passing a value greater than zero is useful for managing a finite pool of resources, where the pool size is equal to the value.

Usage:

import Foundationimport PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution = truelet queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)let semaphore = DispatchSemaphore(value: 0)queue.async {    print("#1 started")    Thread.sleep(forTimeInterval: 5)    print("#1 finished")    semaphore.signal()}queue.async {    print("#2 started")    Thread.sleep(forTimeInterval: 2)    print("#2 finished")    semaphore.signal()}queue.async {    semaphore.wait()    semaphore.wait()        print("#3 finished")}/* prints: #1 started #2 started #2 finished #1 finished #3 finished */

#6. Using OperationQueue and Operation's addDependency(_:)

The Apple Developer API Reference states about Operation​Queue:

Operation queues use the libdispatch library (also known as Grand Central Dispatch) to initiate the execution of their operations.

Usage:

import Foundationimport PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution = truelet operationQueue = OperationQueue()let blockOne = BlockOperation {    print("#1 started")    Thread.sleep(forTimeInterval: 5)    print("#1 finished")}let blockTwo = BlockOperation {    print("#2 started")    Thread.sleep(forTimeInterval: 2)    print("#2 finished")}let blockThree = BlockOperation {    print("#3 finished")}blockThree.addDependency(blockOne)blockThree.addDependency(blockTwo)operationQueue.addOperations([blockThree, blockTwo, blockOne], waitUntilFinished: false)/* prints: #1 started #2 started #2 finished #1 finished #3 finished or #2 started #1 started #2 finished #1 finished #3 finished */

#7. Using OperationQueue and OperationQueue's addBarrierBlock(_:) (requires iOS 13)

import Foundationimport PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution = truelet operationQueue = OperationQueue()let blockOne = BlockOperation {    print("#1 started")    Thread.sleep(forTimeInterval: 5)    print("#1 finished")}let blockTwo = BlockOperation {    print("#2 started")    Thread.sleep(forTimeInterval: 2)    print("#2 finished")}operationQueue.addOperations([blockTwo, blockOne], waitUntilFinished: false)operationQueue.addBarrierBlock {    print("#3 finished")}/* prints: #1 started #2 started #2 finished #1 finished #3 finished or #2 started #1 started #2 finished #1 finished #3 finished */