You use it when you want to execute a block and wait for the results.
One example of this is the pattern where you're using a dispatch queue instead of locks for synchronization. For example, assume you have a shared NSMutableArray a
, with access mediated by dispatch queue q
. A background thread might be appending to the array (async), while your foreground thread is pulling the first item off (synchronously):
NSMutableArray *a = [[NSMutableArray alloc] init];
// All access to `a` is via this dispatch queue!
dispatch_queue_t q = dispatch_queue_create("com.foo.samplequeue", NULL);
dispatch_async(q, ^{ [a addObject:something]; }); // append to array, non-blocking
__block Something *first = nil; // "__block" to make results from block available
dispatch_sync(q, ^{ // note that these 3 statements...
if ([a count] > 0) { // ...are all executed together...
first = [a objectAtIndex:0]; // ...as part of a single block...
[a removeObjectAtIndex:0]; // ...to ensure consistent results
}
});
The point being made here is the same one that Chris Hanson states in his article "When to use NSOperation vs. GCD":
The straightforward answer is a general guideline for all application
development:
Always use the highest-level abstraction available to you, and drop
down to lower-level abstractions when measurement shows that they are
needed.
In this particular case, it means that when writing Cocoa
applications, you should generally be using NSOperation rather than
using GCD directly. Not because of a difference in efficiency, but
because NSOperation provides a higher-level abstraction atop the
mechanisms of GCD.
In general, I agree with this. NSOperation and NSOperationQueue provide support for dependencies and one or two other things that GCD blocks and queues don't have, and they abstract away the lower-level details of how the concurrent operations are implemented. If you need that functionality, NSOperation is a very good way to go.
However, after working with both, I've found myself replacing all of my NSOperation-based code with GCD blocks and queues. I've done this for two reasons: there is significant overhead when using NSOperation for frequent actions, and I believe my code is cleaner and more descriptive when using GCD blocks.
The first reason comes from profiling in my applications, where I found that the NSOperation object allocation and deallocation process took a significant amount of CPU resources when dealing with small and frequent actions, like rendering an OpenGL ES frame to the screen. GCD blocks completely eliminated that overhead, leading to significant performance improvements.
The second reason is more subjective, but I believe that my code is cleaner when using blocks than NSOperations. The quick capture of scope allowed by a block and the inline nature of them make for less code, because you don't need to create custom NSOperation subclasses or bundle up parameters to be passed into the operation, and more descriptive code in my opinion, because you can place the code to be run in a queue at the point where it is fired off.
Again, its a matter of preference, but I've found myself using GCD more, even in otherwise more abstracted Cocoa applications.
Best Answer
It is a library, but there are some kernel optimizations to allow for system level control. In particular, what happens is that there is an addition interface
pthread_workqueue
that allows GCD to tell the kernel it wants a thread to run some particular function, but doesn't actually start a thread (it is basically a continuation). At that point the kernel can choose to start that continuation or not depending on system load.So yes, there is a global system wide infrastructure that manages GCD threads in the kernel, and the second answer is the correct one. The mistake you are making is thinking that there is synchronization going on there that is going to cost something. The scheduler is going to run no matter what, what GCD has done is used a new interface that lets the scheduler not only decide whether or not to run the threads based on their relative priority, but whether or not to create or destroy the threads as well.
It is a (significant) optimization, but it is not strictly necessary, and the FreeBSD port doesn't actually have support for the system wide stuff. If you want to look at the actual interfaces, here is pthread_workqueue.h, the implementation is in Apple's pthread.c, and you can see the stub entry point the kernel uses for starting up the workqueues in their asm stubs in start_wqthread.s. You can also go crawling through xnu to see how it upcalls into the stub if you really want.