标签:
参考书籍:《精通Objective-C》【美】 Keith Lee
使用NSObject类中的performSelectorInBackground: withObject:
方法可以隐式地创建和启动用于执行对象中方法的新线程。该线程会作为后台次要进程立刻启动,而当前进程会立刻返回。下面是一个使用该方法的实例:
首先创建一个继承于NSObject类的,含有将由独立线程执行的方法的类:
#import <Foundation/Foundation.h>
@interface ConcurrentProcessor : NSObject
@property(readwrite) BOOL isFinished;
@property(readonly) NSInteger computeResult;
-(void)computeTask:(id)data;
@end
#import "ConcurrentProcessor.h"
@interface ConcurrentProcessor()
@property(readwrite)NSInteger computeResult;
@end
@implementation ConcurrentProcessor
{
NSString *computeID; // @synchronized指令锁定的唯一对象
NSUInteger computeTasks; // 并行计算任务的计数
NSLock *computeLock; // 锁对象
}
-(id)init{
if ((self = [super init])) {
_isFinished = NO;
_computeResult = 0;
computeLock = [NSLock new];
computeID = @"1";
computeTasks = 0;
}
return self;
}
-(void)computeTask:(id)data{
NSAssert(([data isKindOfClass:[NSNumber class]]), @"Not an NSNumber instance");
NSUInteger computations = [data unsignedIntegerValue];
// 配置线程环境时应在线程入口点创建自动释放池与异常处理程序
@autoreleasepool {
@try {
if ([[NSThread currentThread] isCancelled]) {
return;
}
// @synchronized指令括号内为唯一标识符,标识符保护的代码块对象只能同时被最多一个线程访问
@synchronized (computeID) {
// 增加活动任务的计数
computeTasks++;
}
// 获取锁并执行关键代码部分中的计算操作
[computeLock lock];
if ([[NSThread currentThread] isCancelled]) {
[computeLock unlock];
return;
}
NSLog(@"Performing computations %lu",computations);
for (int ii = 0; ii < computations; ii++) {
self.computeResult = self.computeResult + 1;
}
// 完成计算并解除锁
[computeLock unlock];
// 模拟额外的处理时间
[NSThread sleepForTimeInterval:1.0];
// 减少活动任务数,如果数量为0,则更新标志位
@synchronized (computeID) {
computeTasks--;
if (!computeTasks) {
self.isFinished = YES;
}
}
} @catch (NSException *exception) {}
}
}
@end
最后在main.m中进行测试:
#import <Foundation/Foundation.h>
#import "ConcurrentProcessor.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
ConcurrentProcessor *processor = [ConcurrentProcessor new];
// 隐式地创建并启动3个线程
[processor performSelectorInBackground:@selector(computeTask:) withObject:[NSNumber numberWithInt:5]];
[processor performSelectorInBackground:@selector(computeTask:) withObject:[NSNumber numberWithInt:10]];
[processor performSelectorInBackground:@selector(computeTask:) withObject:[NSNumber numberWithInt:20]];
while (!processor.isFinished);
NSLog(@"Computation result = %ld",processor.computeResult);
}
return 0;
}
运行结果:
2016-07-19 15:15:47.700 ConcurrentThreads[17142:161246] Performing computations 10
2016-07-19 15:15:47.700 ConcurrentThreads[17142:161247] Performing computations 20
2016-07-19 15:15:47.701 ConcurrentThreads[17142:161245] Performing computations 5
2016-07-19 15:15:48.774 ConcurrentThreads[17142:161222] Computation result = 35
可以直接使用NSThread类中的API显示地创建并管理线程,与隐式方法等价。
#import <Foundation/Foundation.h>
#import "ConcurrentProcessor.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
ConcurrentProcessor *processor = [ConcurrentProcessor new];
// 隐式地创建并启动1个线程
[processor performSelectorInBackground:@selector(computeTask:) withObject:[NSNumber numberWithInt:5]];
// 显示创建并启动1个线程
[NSThread detachNewThreadSelector:@selector(computeTask:) toTarget:processor withObject:[NSNumber numberWithInt:10]];
// 显示创建1个线程,需要用start方法手动启动
NSThread *computeThread = [[NSThread alloc] initWithTarget:processor selector:@selector(computeTask:) object:[NSNumber numberWithInt:20]];
[computeThread setThreadPriority:0.5];
[computeThread start];
while (!processor.isFinished);
NSLog(@"Computation result = %ld",processor.computeResult);
}
return 0;
}
运行结果:
2016-07-19 15:18:12.607 ConcurrentThreads[17269:162710] Performing computations 10
2016-07-19 15:18:12.608 ConcurrentThreads[17269:162711] Performing computations 20
2016-07-19 15:18:12.608 ConcurrentThreads[17269:162709] Performing computations 5
2016-07-19 15:18:13.681 ConcurrentThreads[17269:162677] Computation result = 35
NSOperation类(及其子类)的实例可以为单个任务封装代码,在处理非并发任务时,具体子类通常只需要重写main方法。而在处理并发任务时,至少必须重写start、isConcurrent、isExecuting、isFinishied方法。后面3个方法必须返回与操作状态有关的值,而且这3个方法必须具备线程安全性。当这些值改变时,这些方法还必须生成适当的键值观察(KVO)通知。下面是用NSOperation类的子类实现并发的示例。
首先创建NSOperation类的子类GreetingOperation类:
#import <Foundation/Foundation.h>
@interface GreetingOperation : NSOperation
@end
#import "GreetingOperation.h"
@implementation GreetingOperation
{
BOOL finished;
BOOL executing;
}
-(id)init{
if ((self = [super init])) {
executing = NO;
finished = NO;
}
return self;
}
-(void)start{
// 如果操作被取消了就返回结果
if ([self isCancelled]) {
// 修改isFinished方法返回值时必须发送KVO通知
[self willChangeValueForKey:@"isFinished"];
finished = YES;
[self didChangeValueForKey:@"isFinished"];
return;
}
// 修改isExecuting方法返回值时必须发送KVO通知
[self willChangeValueForKey:@"isExecuting"];
// 使用独立线程执行main方法中的操作
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
executing = YES;
[self didChangeValueForKey:@"isExecuting"];
}
-(void)main{
@autoreleasepool {
@try {
if (![self isCancelled]) {
NSLog(@"Hello,World");
// 暂停,以便模拟执行任务的过程
[NSThread sleepForTimeInterval:3.0];
NSLog(@"Goodbye,World");
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
executing = NO;
finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
} @catch (NSException *exception) {
}
}
}
-(BOOL)isConcurrent{
return YES;
}
-(BOOL)isExecuting{
return executing;
}
-(BOOL)isFinished{
return finished;
}
@end
最后在main.m中进行测试:
#import <Foundation/Foundation.h>
#import "GreetingOperation.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
GreetingOperation *greetingOp = [GreetingOperation new];
[greetingOp start];
GreetingOperation *greetingOp2 = [GreetingOperation new];
[greetingOp2 start];
// 如果没有这行代码,main函数会在操作中的main方法执行完成之前就返回
while (![greetingOp isFinished] || ![greetingOp2 isFinished]);
}
return 0;
}
运行结果:
2016-07-19 16:15:45.642 GreetingOperation[20255:195915] Hello,World
2016-07-19 16:15:45.642 GreetingOperation[20255:195914] Hello,World
2016-07-19 16:15:48.647 GreetingOperation[20255:195914] Goodbye,World
2016-07-19 16:15:48.647 GreetingOperation[20255:195915] Goodbye,World
操作队列是一种提供并发执行操作能力的机制。可以将NSOperation类或其子类的对象添加到NSOperationQueue对象中。下面是操作队列的使用示例。
首先创建一个NSOperation类的子类ConcurrentProcessor类:
#import <Foundation/Foundation.h>
@interface ConcurrentProcessor : NSOperation
@property(readonly) NSUInteger computations;
-(id)initWithData:(NSInteger *)result computations:(NSUInteger)computations;
@end
#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 = *computeResult + 1;
}
}
} @catch (NSException *exception) {}
}
}
@end
最后在main.m中进行测试:
#import <Foundation/Foundation.h>
#import "ConcurrentProcessor.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 创建操作队列类对象
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInteger result = 0;
// 创建3个操作类对象
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];
// 添加操作间的依赖性,proc1必须先于proc2
[proc2 addDependency:proc1];
// 将操作对象添加到操作队列中执行
[queue addOperations:operations waitUntilFinished:NO];
// 等待,当所有操作完成时显示结果
[queue waitUntilAllOperationsAreFinished];
NSLog(@"Computation result = %ld", result);
}
return 0;
}
运行结果:
2016-07-19 16:20:28.006 ConcurrentOperations[20508:198875] Performing 5 computations
2016-07-19 16:20:28.006 ConcurrentOperations[20508:198877] Performing 20 computations
2016-07-19 16:20:29.011 ConcurrentOperations[20508:198877] Performing 10 computations
2016-07-19 16:20:30.016 ConcurrentOperations[20508:198815] Computation result = 32
可以看到,proc1与proc3是并发执行的,而proc2必须等proc1执行完成后才能执行。
Grand Central Dispatch(GCD)是一个集合,它含有语言特性、基于C语言API,以及支持使用分派队列执行任务的系统增强功能。下面是使用GCD的示例。
GCD的使用非常方便,不需要创建其他类:
#import <Foundation/Foundation.h>
typedef void (^ComputeTask)(void);
// 函数返回用于累加的语句块
ComputeTask getComputeTask(NSInteger *result, NSUInteger computations){
NSInteger *computeResult = result;
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;
NSInteger computeResult2;
// 创建顺序队列和分组
dispatch_queue_t serialQueue = dispatch_queue_create("MySerialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("MySerialQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_group_t group = dispatch_group_create();
// 分别向两个队列中添加任务
dispatch_group_async(group, serialQueue, getComputeTask(&computeResult, 1));
dispatch_group_async(group, serialQueue, getComputeTask(&computeResult, 3));
dispatch_group_async(group, serialQueue, getComputeTask(&computeResult, 5));
dispatch_group_async(group, serialQueue2, getComputeTask(&computeResult2, 2));
dispatch_group_async(group, serialQueue2, getComputeTask(&computeResult2, 4));
dispatch_group_async(group, serialQueue2, getComputeTask(&computeResult2, 6));
// 等待,当分组中的所有任务都完成时显示结果
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"Computation result = %ld", computeResult);
NSLog(@"Computation result2 = %ld", computeResult2);
}
return 0;
}
运行结果:
2016-07-19 16:38:35.074 ConcurrentDispatch[21460:209103] Performing 1 computations
2016-07-19 16:38:35.074 ConcurrentDispatch[21460:209104] Performing 2 computations
2016-07-19 16:38:36.080 ConcurrentDispatch[21460:209103] Performing 3 computations
2016-07-19 16:38:36.080 ConcurrentDispatch[21460:209104] Performing 4 computations
2016-07-19 16:38:37.083 ConcurrentDispatch[21460:209104] Performing 6 computations
2016-07-19 16:38:37.083 ConcurrentDispatch[21460:209103] Performing 5 computations
2016-07-19 16:38:37.083 ConcurrentDispatch[21460:209079] Computation result = 9
2016-07-19 16:38:37.083 ConcurrentDispatch[21460:209079] Computation result2 = 12
分析结果可以看出,2个队列是并发执行的,而同一个队列中的任务是异步执行的。
操作队列和GCD提供了异步、基于队列的机制,不仅替代了低级线程管理,并且与基于线程的编程方式相比,最大化了系统利用率和效率。操作队列是基于对象的,与GCD相比,会有更多的系统开销,占用更多的资源,不过它的面向对象的高级API与Objective-C平台一致,因而更易于使用,而且支持复杂的操作间的依赖关系、基于约束的执行和操作对象管理。
GCD提供了低级API(基于C语言)并且是轻量级的。所以性能更好,且需要使用的代码更少。
由于操作队列和GCD都不处理实时约束,所以在实时系统中,线程仍旧是合适的并发编程机制。
除此之外,Foundation框架还提供了多种执行异步处理的API,如NSFileHandle类和NSPort类,在可以使用异步API时应该尽量使用。
标签:
原文地址:http://blog.csdn.net/sps900608/article/details/51957347