Using Operation Queues for Concurrency

来源:互联网 发布:英语 伴读软件 编辑:程序博客网 时间:2024/05/17 02:43

Using Operation Queues for Concurrency

You have implemented a concurrent program using threads and a concurrent operation, so now you will implement a program that uses operations and an operation queue for concurrency. This program contains the same functionality of the ConcurrentThreads program that you implemented earlier in this chapter. This will enable you to compare the use of the different APIs and mechanisms for concurrent programming.

In Xcode, create a new project by selecting New image Project . . .from the Xcode File menu. In the New Project Assistant pane, create a command-line application. In the Project Optionswindow, specify ConcurrentOperations 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.

Next you will create the custom operation class. Select New image File . . . from the Xcode File menu, select the Objective-C class template, and name the class ConcurrentProcessor. Make the class a subclass of NSOperation, select theConcurrentOperations folder for the files location and theConcurrentOperations project as the target, and then click theCreate button. In the Xcode project navigator pane, select theConcurrentProcessor.m file and update the interface, as shown in Listing 17-21.

Listing 17-21.  ConcurrentProcessor Interface

#import <Foundation/Foundation.h>@interface ConcurrentProcessor : NSOperation@property (readonly) NSUInteger computations;- (id)initWithData:(NSInteger *)result computations:(NSUInteger)computations;@end

The interface contains one property named computations and a single initialization method. The interface is a subclass ofNSOperation, as required for a custom operation class. Thecomputations property specifies the number of computations the operation will perform. In the Xcode project navigator, select theConcurrentProcessor.m file and update the implementation, as shown in Listing 17-22.

Listing 17-22.  ConcurrentProcessor Implementation

#import "ConcurrentProcessor.h"@implementation ConcurrentProcessor{  NSInteger *computeResult;}- (id)initWithData:(NSInteger *)result computations:(NSUInteger)computations{  if ((self = [super init]))  {    _computations = computations;    computeResult = result;  }  return self;}- (void)main{  @autoreleasepool  {    @try    {      if (![self isCancelled])      {        NSLog(@"Performing %ld computations", self.computations);        [NSThread sleepForTimeInterval:1.0];        for (int ii=0; ii<self.computations; ii++)        {          *computeResult = *computeResult + 1;        }      }    }    @catch (NSException *ex) {}  }}@end

The implementation begins by declaring a private instance variable,computeResult, which contains the address of the memory location where the computation result is stored. The init: method sets the computations property and computeResult variable to the input parameters. The main method performs the compute task for the operation. It includes an autorelease pool and a try-catch block, as recommended for thread-based execution of operation objects. The main method also checks if the operation is cancelled in order to quickly terminate its execution if it is no longer needed. The computation logic simply increments the computation result for the number of computations specified. Notice here that, unlike with the concurrent operation shown in Listing 17-19 (theGreetingOperation program), thread execution state (i.e.,isFinished and isExecuting) is not updated. This is performed automatically by the operation queue. Also note that, unlike the thread-based ConcurrentProcessor implementation (as shown inListing 17-14), synchronization mechanisms are not required. This is due to the fact that interoperation dependencies can be declared. These prevent operations from concurrently accessing shared data and also synchronize the order that operations are executed.

Now let’s move on to the main() function. In the Xcode project navigator, select the main.m file and update the main() function, as shown in Listing 17-23.

Listing 17-23.  ConcurrentOperations main( ) Function

#import <Foundation/Foundation.h>#import "ConcurrentProcessor.h"int main(int argc, const char * argv[]){  @autoreleasepool  {    NSOperationQueue *queue = [[NSOperationQueue alloc] init];    NSInteger result = 0;         // Create operation objects    ConcurrentProcessor *proc1 = [[ConcurrentProcessor alloc]initWithData:&result                                                             computations:5];    ConcurrentProcessor *proc2 = [[ConcurrentProcessor alloc]initWithData:&result                                                             computations:10];    ConcurrentProcessor *proc3 = [[ConcurrentProcessor alloc]initWithData:&result                                                             computations:20];    NSArray *operations = @[proc1, proc2, proc3];         // Add inter-operation dependencies    [proc2 addDependency:proc1];    [proc3 addDependency:proc2];         // Add operations to queue to start execution    [queue addOperations:operations waitUntilFinished:NO];         // Wait until all operations are finished, then display result    [queue waitUntilAllOperationsAreFinished];    NSLog(@"Computation result = %ld", result);  }  return 0;}

The method begins by creating an operation queue and the variable, computeResult, which holds the result of the computation for the operations. Then three operation objects are created, each performing a different number of computations, and these are combined in an NSArray instance. Next, dependencies between the operations are defined. In this case, operation 1 (proc1) must complete before operation 2 (proc2), and operation 2 must complete before operation 3 (proc3). The operations are then added to the queue to begin asynchronous execution. The code waits until all of the operations have finished execution and then logs the computation result to the output pane.

When you compile and run the program, you should observe the messages in the output pane shown in Figure 17-7.

9781430250500_Fig17-07.jpg

Figure 17-7. ConcurrentOperations program output

The results are identical to that obtained with the thread-based version of this program, with considerably less code complexity. This program demonstrates how operation objects and queues can greatly simplify concurrent programming. In effect, they enable you to execute tasks asynchronously and concurrently without having to perform low-level thread-based programming, and manage the resulting complexity. They enable you to manage dependencies among various operations, cancel or suspend them, and provide a higher-level, object-oriented abstraction for concurrent programming. In the next section, you will explore Grand Central Dispatch, a C-based mechanism for asynchronous/concurrent programming.

原创粉丝点击