Grand Central Dispatch
Grand Central Dispatch (GCD) is a set of language features, C-based APIs, and system enhancements that support the use of dispatch queues for executing tasks. GCD dispatch queues can be used to execute code synchronously or asynchronously, and to perform tasks serially or concurrently. As with operation queues, dispatch queues are easier to use than threads and more efficient at executing asynchronous or concurrent tasks.
Apple provides a complete set of documentation on the GCD APIs and their use for concurrent programming. In order to provide a simple comparison of concurrent programming using operation queues and dispatch queues, you will now reimplement the ConcurrentOperations program that you developed earlier, this time with dispatch queues.
In Xcode, create a new project by selecting New Project . . .from the Xcode File menu. In the New Project Assistant pane, create a command-line application. In the Project Optionswindow, specify ConcurrentDispatch for the Product Name, choose Foundation for the Project Type, and select ARC memory management by checking the Use Automatic Reference Counting check box. Specify the location in your file system where you want the project to be created (if necessary, select New Folder and enter the name and location for the folder), uncheck the Source Control check box, and then click the Create button.
In the Xcode project navigator, select the main.m file and update it as shown in Listing 17-24.
Listing 17-24. ConcurrentDispatch main.m File
#import <Foundation/Foundation.h>
typedef void (^ComputeTask)(void);
/* Retrieve a block used to download a URL */
ComputeTask getComputeTask(NSInteger *result, NSUInteger computation)
{
NSInteger *computeResult = result;
NSUInteger computations = computation;
return ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"Performing %ld computations", computations);
for (int ii=0; ii<computations; ii++)
{
*computeResult = *computeResult + 1;
}
};
}
int main(int argc, const char * argv[])
{
@autoreleasepool
{
NSInteger computeResult;
// Create serial queue and group
dispatch_queue_t serialQueue = dispatch_queue_create("MySerialQueue",
DISPATCH_QUEUE_SERIAL);
dispatch_group_t group = dispatch_group_create();
// Add tasks to queue
dispatch_group_async(group, serialQueue, getComputeTask(&computeResult, 5));
dispatch_group_async(group, serialQueue, getComputeTask(&computeResult, 10));
dispatch_group_async(group, serialQueue, getComputeTask(&computeResult, 20));
// Block until all tasks from group are completed, then display results
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"Computation result = %ld", computeResult);
}
return 0;
}
The listing includes, in addition to the main() function, a function named computeTask that is identical in functionality to that provided by the task in the ConcurrentOperations program, as shown in the main method in Listing 17-22.
The main() function uses GCD APIs to create and asynchronously dispatch three tasks for serial execution, thereby coordinating execution properly and preventing concurrent access to shared data. It creates a serial dispatch queue and a dispatch group.
dispatch_queue_t serialQueue = dispatch_queue_create("MySerialQueue",
DISPATCH_QUEUE_SERIAL);
dispatch_group_t group = dispatch_group_create();
The code then dispatches the three tasks to the queue.
dispatch_group_async(group, serialQueue, getComputeTask(&computeResult, 5));
dispatch_group_async(group, serialQueue, getComputeTask(&computeResult, 10));
dispatch_group_async(group, serialQueue, getComputeTask(&computeResult, 20));
Notice that the task to be executed is specified by a block literal retrieved by the computeTask() function, each time providing a different argument for the number of computations. Again, this is identical to what was done for the ConcurrentOperations program.The GCD dispatch_group_async() function causes these tasks to be performed asynchronously and, as the queue is a serial queue, in serial order. Next, the GCD dispatch_group_wait() function is used to block the main thread until the tasks complete.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
Now save, compile, and run the BlockConcurrentTasks program and observe the messages in the output pane (as shown in Figure 17-8).
Figure 17-8. Testing the ConcurrentDispatch project
The messages in the output pane show that the concurrent tasks were performed successfully. Compared to the ConcurrentOperations program, the version using GCD dispatch queues. Although it employs C-based APIs, it required much less code.