码迷,mamicode.com
首页 > 其他好文 > 详细

[精通Objective-C]三种实现并发编程的方式

时间:2016-07-19 17:07:17      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:

[精通Objective-C]三种实现并发编程的方式

参考书籍:《精通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执行完成后才能执行。

分派队列GCD

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时应该尽量使用。

[精通Objective-C]三种实现并发编程的方式

标签:

原文地址:http://blog.csdn.net/sps900608/article/details/51957347

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!