Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is dispatch_apply synchronous or asynchronous?

I was told that I could use Grand Central Dispatch to run n processes simultaneously, in an asynchronous fashion. The documentation said that if the processes were in a for loop, I could use the function dispatch_apply. But now it's saying

Note that dispatch_apply is synchronous, so all the applied blocks will have completed by the time it returns.

Does this mean the blocks that are submitted to a queue using dispatch_apply are executed in order? If so, what is the point of using concurrency? Won't the slowdown be the same?

like image 398
Jessica Avatar asked Aug 05 '13 00:08

Jessica


2 Answers

dispatch_apply is, as stated in the docs, synchronous. It runs a block on the specified queue in parallel (if possible) and waits until all the blocks return. If you want to run a block just once asynchronously, use dispatch_async, if you want to run a block multiple times in parallel without blocking your current queue, just call dispatch_apply within dispatch_async:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(size_t size) {
        NSLog(@"%lu", size);
    });
});
like image 174
Sebastian Avatar answered Nov 17 '22 14:11

Sebastian


The purpose of the synchronous dispatch_apply is to asynchronously dispatch the inner loop interations to available parallel processing resources. Thus, the overall loop performance may speed up.

Faster loop performance? Very possibly, Yes. (see caveat)

Blocks the thread calling dispatch_apply? Yes, just like loop blocks until completed.

For GCD, dispatch_apply is synchronous since dispatch_apply will not return until all the asynchronous, parallel tasks that dispatch_apply creates have completed.

However, each individual task enqueued by dispatch_apply can run as concurrent asynchronous tasks if the target queue is asynchronous.

For example in Swift:

let batchCount: Int = 10 
let queue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
dispatch_apply(batchCount, queue) { 
   (i: Int) -> Void in
   print(i,  terminator: "  ")
}
print("\ndispatch_apply QOS_CLASS_UTILITY queue completed")

yields unordered output like:

0  8  1  9  2  3  4  5  6  7  
dispatch_apply QOS_CLASS_UTILITY queue completed

So, dispatch_apply synchronously blocks when called, but the "batch" of tasks generated by dispatch_apply can run concurrently, asynchronously, in parallel to each other.

Keep in mind the caveat that ...

the work performed during each iteration is distinct from the work performed during all other iterations, and the order in which each successive loop finished is unimportant

Also, note that use of a serial queue for the inner loop tasks will not have any performance gain.

Although using a serial queue is permissible and does the right thing for your code, using such a queue has no real performance advantages over leaving the loop in place.

like image 7
l --marc l Avatar answered Nov 17 '22 12:11

l --marc l