多线程编程之 NSOperationQueue

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.

    NSInvocationOperation * operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printf) object:nil];
    
    [operation start];
    NSLog(@"hahahhah");
}

-(void)printf
{
    for (int i = 0 ; i < 10; i++) {
        NSLog(@"%d",i);
    }
}
2014-08-03 21:42:55.446 HelloWorld[4918:60b] 0
2014-08-03 21:42:55.448 HelloWorld[4918:60b] 1
2014-08-03 21:42:55.450 HelloWorld[4918:60b] 2
2014-08-03 21:42:55.451 HelloWorld[4918:60b] 3
2014-08-03 21:42:55.452 HelloWorld[4918:60b] 4
2014-08-03 21:42:55.453 HelloWorld[4918:60b] 5
2014-08-03 21:42:55.454 HelloWorld[4918:60b] 6
2014-08-03 21:42:55.455 HelloWorld[4918:60b] 7
2014-08-03 21:42:55.457 HelloWorld[4918:60b] 8
2014-08-03 21:42:55.458 HelloWorld[4918:60b] 9
2014-08-03 21:42:55.459 HelloWorld[4918:60b] hahahhah

加入队列-------------------执行异步操作

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.

    NSInvocationOperation * operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printf) object:nil];
    NSOperationQueue * queue = [[NSOperationQueue alloc]init];
    [queue addOperation:operation];
//    [operation start];
    for (int i = 100 ; i < 110; i++) {
        NSLog(@"%d",i);
    }
}

-(void)printf
{
    for (int i = 0 ; i < 10; i++) {
        NSLog(@"%d",i);
    }
}

2014-08-03 21:46:03.493 HelloWorld[4931:1803] 0
2014-08-03 21:46:03.496 HelloWorld[4931:1803] 1
2014-08-03 21:46:03.493 HelloWorld[4931:60b] 100
2014-08-03 21:46:03.497 HelloWorld[4931:1803] 2
2014-08-03 21:46:03.498 HelloWorld[4931:60b] 101
2014-08-03 21:46:03.498 HelloWorld[4931:1803] 3
2014-08-03 21:46:03.500 HelloWorld[4931:1803] 4
2014-08-03 21:46:03.500 HelloWorld[4931:60b] 102
2014-08-03 21:46:03.501 HelloWorld[4931:1803] 5
2014-08-03 21:46:03.503 HelloWorld[4931:60b] 103
2014-08-03 21:46:03.504 HelloWorld[4931:1803] 6
2014-08-03 21:46:03.505 HelloWorld[4931:1803] 7
2014-08-03 21:46:03.506 HelloWorld[4931:60b] 104
2014-08-03 21:46:03.506 HelloWorld[4931:60b] 105
2014-08-03 21:46:03.507 HelloWorld[4931:1803] 8
2014-08-03 21:46:03.509 HelloWorld[4931:1803] 9
2014-08-03 21:46:03.508 HelloWorld[4931:60b] 106
2014-08-03 21:46:03.510 HelloWorld[4931:60b] 107
2014-08-03 21:46:03.511 HelloWorld[4931:60b] 108
2014-08-03 21:46:03.512 HelloWorld[4931:60b] 109

创建一个操作队列:

  1. NSOperationQueue *queue = [[NSOperationQueue alloc] init];  

二、添加NSOperationNSOperationQueue

1.添加一个operation

  1. [queue addOperation:operation];  

2.添加一组operation

  1. [queue addOperations:operations waitUntilFinished:NO];  

3.添加一个block形式的operation

  1. [queue addOperationWithBlock:^() {  
  2.     NSLog(@"执行一个新的操作,线程:%@", [NSThread currentThread]);  
  3. }]; 
NSOperation添加到queue之后,通常短时间内就会得到运行。但是如果存在依赖,或者整个queue被暂停等原因,也可能需要等待。

注意NSOperation添加到queue后,绝对不要再修改NSOperation对象的状态。因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或数据会产生不利的影响。你只能查看NSOperation对象的状态, 比如是否正在运行、等待运行、已经完成等


三、添加NSOperation的依赖对象
1.当某个NSOperation对象依赖于其它NSOperation对象的完成时,就可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作,当前NSOperation对象才会开始执行操作。另外,通过removeDependency方法来删除依赖对象。

[operation2 addDependency:operation1];  

依赖关系不局限于相同queue中的NSOperation对象,NSOperation对象会管理自己的依赖, 因此完全可以在不同的queue之间的NSOperation对象创建依赖关系


唯一的限制是不能创建环形依赖,比如A依赖B,B依赖A,这是错误的


2.依赖关系会影响到NSOperation对象在queue中的执行顺序,看下面的例子:

1> 没有设置依赖关系

  1. NSOperationQueue *queue = [[NSOperationQueue alloc] init];  
  2.   
  3. NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){  
  4.     NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);  
  5. }];  
  6.   
  7. NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){  
  8.     NSLog(@"执行第2次操作,线程:%@", [NSThread currentThread]);  
  9. }];  
  10.   
  11. [queue addOperation:operation1];  
  12. [queue addOperation:operation2];  
打印信息:
  1. 2013-02-03 00:21:35.024 thread[5616:3d13] 执行第1次操作,线程:<NSThread: 0x7658570>{name = (null), num = 3}  
  2. 2013-02-03 00:21:35.063 thread[5616:1303] 执行第2次操作,线程:<NSThread: 0x765a2e0>{name = (null), num = 4}  
可以看出,默认是按照添加顺序执行的,先执行operation1,再执行operation2

2> 设置了依赖关系

  1. NSOperationQueue *queue = [[NSOperationQueue alloc] init];  
  2.   
  3. NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){  
  4.     NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);  
  5. }];  
  6.   
  7. NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){  
  8.     NSLog(@"执行第2次操作,线程:%@", [NSThread currentThread]);  
  9. }];  
  10. // operation1依赖于operation2  
  11. [operation1 addDependency:operation2];  
  12.   
  13. [queue addOperation:operation1];  
  14. [queue addOperation:operation2];  
打印信息:
  1. 2013-02-03 00:24:16.260 thread[5656:1b03] 执行第2次操作,线程:<NSThread: 0x7634490>{name = (null), num = 3}  
  2. 2013-02-03 00:24:16.285 thread[5656:1303] 执行第1次操作,线程:<NSThread: 0x9138b50>{name = (null), num = 4}  
可以看出,先执行operation2,再执行operation1

四、修改Operations的执行顺序

对于添加到queue中的operations,它们的执行顺序取决于2点:

1.首先看看NSOperation是否已经准备好:是否准备好由对象的依赖关系确定

2.然后再根据所有NSOperation的相对优先级来确定。优先级等级则是operation对象本身的一个属性。默认所有operation都拥有“普通”优先级,不过可以通过setQueuePriority:方法来提升或降低operation对象的优先级。优先级只能应用于相同queue中的operations。如果应用有多个operation queue,每个queue的优先级等级是互相独立的。因此不同queue中的低优先级操作仍然可能比高优先级操作更早执行。

注意:优先级不能替代依赖关系,优先级只是对已经准备好的 operations确定执行顺序。先满足依赖关系,然后再根据优先级从所有准备好的操作中选择优先级最高的那个执行。

五、设置队列的最大并发操作数量

队列的最大并发操作数量,意思是队列中最多同时运行几条线程

虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue

  1. // 每次只能执行一个操作  
  2. queue.maxConcurrentOperationCount = 1;  
  3. // 或者这样写  
  4. [queue setMaxConcurrentOperationCount:1];  

六、取消Operations

一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,唯一能做的事情是取消。你可以调用Operation对象的cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作。

  1. // 取消单个操作  
  2. [operation cancel];  
  3.   
  4. // 取消queue中所有的操作  
  5. [queue cancelAllOperations];  

七、等待Options完成

为了最佳的性能,你应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其它事情。如果需要在当前线程中处理operation完成后的结果,可以使用NSOperation的waitUntilFinished方法阻塞当前线程,等待operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。

  1. // 会阻塞当前线程,等到某个operation执行完毕  
  2. [operation waitUntilFinished];  

除了等待单个Operation完成,你也可以同时等待一个queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。注意:在等待一个 queue时,应用的其它线程仍然可以往queue中添加Operation,因此可能会加长线程的等待时间。

  1. // 阻塞当前线程,等待queue的所有操作执行完毕  
  2. [queue waitUntilAllOperationsAreFinished];  

八、暂停和继续queue

如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行

  1. // 暂停queue  
  2. [queue setSuspended:YES];  
  3.   
  4. // 继续queue  
  5. [queue setSuspended:NO]; 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值