1.简介
NS(基于OC语言)是对GCD(基于C语言)的封装,让开发者能够更加友好的方便的去使用多线程技术。
2.NSOperation的基本使用
NSOperation是抽象类,所以如果要使用NSOperation则需要继承和实现它。
内置的子类常用的有两个:
- NSInvocationOperation
- NSBlockOperation
使用步骤:
无论上述哪一个一般使用都遵循以下步骤:
- 创建队列/获取主队列
- 创建子类实例
- 加入队列
例子:https://github.com/xufeng79x/NSOperation
3.NSOperation再研究
NSOperation 中有这么一句话:
An operation object is a single-shot object—that is, it executes its task once and cannot be used to execute it again.
所以说一个操作只能被执行一次,当如下代码将某个操作加到多个队列的时候会出现错误
[queue1 addOperation:op1];
[queue2 addOperation:op1];
错误:
2016-03-01 16:30:40.425 NSOperation-Test[2430:148961] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException‘, reason: ‘*** -[NSOperationQueue addOperation:]: operation is already enqueued on a queue‘
4.线程间通信
和GCD一样,当在某个线程中获取到信息需要更新UI的时候,那么更新UI需要在主线程中进行,所以这个就涉及到线程间的通信。
// MARK: 线程间通信 - (void) test_threads_com { //1. 创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //2. 在其他形成做耗时操作 [queue addOperationWithBlock:^{ NSLog(@"耗时操作---------%@", [NSThread currentThread]); [[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@"更新UI---------%@", [NSThread currentThread]); }]; }]; }
上述代码通过队列的不同从而让人物去在不同线程中执行,main队列中的人物都是在主线程中执行的。
5.高级操作
介绍最大并发数,挂起,取消全部线程
注意:以上都是在队列上操作的,只对队列中未运行的线程其作用,而对正在运行和已经运行完毕的线程无效。
最大并发数:
// MARK:最大并发数 - (void)test_MaxThreadsNum { //1. 创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //2. 设定队列上的最大并发数 [queue setMaxConcurrentOperationCount:2]; for (int i = 0; i < 20; i++) { //3. 直接将operation放到queue中 [queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:5.0f]; NSLog(@"------------%@-------%d", [NSThread currentThread], i); }]; } }
输出:
可以看到每一次只有两个线程运行。
挂起:
// MARK:挂起 // 测试步骤:新增全局队列,运行的时候点击暂停/开始按钮来让队列挂起或者取消挂起 - (void)test_suspend { // 设定最大并发数,便于观察 [self.globQueue setMaxConcurrentOperationCount:2]; for (int i = 0; i < 20; i++) { //1直接将operation放到queue中 [self.globQueue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:5.0f]; NSLog(@"------------%@-------%d", [NSThread currentThread], i); }]; } }
测试结果:
当程序运行的时候点击【暂定】后程序不再运行队列中的操作,当我们再次点击【开始】后继续运行队列中剩余的操作。
如前所述,挂起操作也是对于队列中的操作,而对已经运行的操作则不影响,所以我们看到暂停日志后又有两个任务打印出了结束日志。
另外一个细节就是当点击暂定时候队列中显示还有16个,再次点击开始的时候发现队列中还有14个,这说明只有当操作运行完毕后才会从队列中移除。
取消全部:
// MARK:取消全部 // 测试步骤:点击取消全部按钮 - (void)test_cancelALl { // 设定最大并发数,便于观察 [self.globQueue setMaxConcurrentOperationCount:2]; for (int i = 0; i < 20; i++) { //1直接将operation放到queue中 [self.globQueue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:5.0f]; NSLog(@"------------%@-------%d", [NSThread currentThread], i); }]; } }
测试结果:
2016-03-04 11:20:06.469 NSOperation-Test[1379:43730] ------------<NSThread: 0x7b297590>{number = 6, name = (null)}-------0 2016-03-04 11:20:06.469 NSOperation-Test[1379:43912] ------------<NSThread: 0x7b2803d0>{number = 7, name = (null)}-------1 2016-03-04 11:20:10.084 NSOperation-Test[1379:43511] the queue has been cancelall! 18 in queue now! 2016-03-04 11:20:11.471 NSOperation-Test[1379:43912] ------------<NSThread: 0x7b2803d0>{number = 7, name = (null)}-------3 2016-03-04 11:20:11.471 NSOperation-Test[1379:43730] ------------<NSThread: 0x7b297590>{number = 6, name = (null)}-------2 2016-03-04 11:20:12.351 NSOperation-Test[1379:43511] the queue has been cancelall! 0 in queue now!
可以看到当运行的时候点击取消后后续所有任务都不会再运行。
值得注意的是点击取消后,还有运行日志打出,还是和之前所述一样,取消全部不会影响正在运行的操作。
另外需要注意的是,当点击取消的时候,日志还显示还有18个任务,当正在运行的操作完毕后,再次点击取消则显示为0个。
说明只有运行的任务运行完毕后才会清空队列。
思考--队列中的任务会按序执行吗?
// MARK:是否按序执行 - (void)test_executeOrder { // 设定最大并发数为1,便于观察 [self.globQueue setMaxConcurrentOperationCount:1]; for (int i = 0; i < 20; i++) { //1直接将operation放到queue中 [self.globQueue addOperationWithBlock:^{ //[NSThread sleepForTimeInterval:1.0f]; NSLog(@"------------%@-------%d", [NSThread currentThread], i); }]; } }
日志:
2016-03-04 11:32:25.530 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------0 2016-03-04 11:32:25.531 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------1 2016-03-04 11:32:25.531 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------2 2016-03-04 11:32:25.531 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------3 2016-03-04 11:32:25.531 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------4 2016-03-04 11:32:25.532 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------5 2016-03-04 11:32:25.532 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------6 2016-03-04 11:32:25.532 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------7 2016-03-04 11:32:25.532 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------8 2016-03-04 11:32:25.533 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------9 2016-03-04 11:32:25.533 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------10 2016-03-04 11:32:25.533 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------11 2016-03-04 11:32:25.533 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------12 2016-03-04 11:32:25.534 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------13 2016-03-04 11:32:25.534 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------14 2016-03-04 11:32:25.534 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------15 2016-03-04 11:32:25.535 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------16 2016-03-04 11:32:25.535 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------17 2016-03-04 11:32:25.535 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------18 2016-03-04 11:32:25.535 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------19
经过多次测试,发现是先进先出的有序执行。
6.操作依赖
-线性依赖:
在NSOperation中可以进行操作的依赖设置,一个操作依赖于另外一个操作,可以在不同队列间的操作间设置依赖。
// MARK:操作间依赖 - (void)test_dependsLine { //1. 建立第一个队列 NSOperationQueue *queue1 = [[NSOperationQueue alloc]init]; NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"------I am op1 in queue1------%@", [NSThread currentThread]); }]; //2. 建立第二个队列 NSOperationQueue *queue2 = [[NSOperationQueue alloc] init]; NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"------I am op2 in queue2------%@", [NSThread currentThread]); }]; //3. 建立第二个队列 NSOperationQueue *queue3 = [[NSOperationQueue alloc] init]; NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"------I am op3 in queue3------%@", [NSThread currentThread]); }]; // 设定依赖关系 [op3 addDependency:op2]; [op2 addDependency:op1]; // 一旦放入队列则将执行 [queue1 addOperation:op1]; [queue2 addOperation:op2]; [queue3 addOperation:op3]; }
结果:
2016-03-05 00:26:01.803 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:01.804 NSOperation-Test[660:12845] ------I am op2 in queue2------<NSThread: 0x79f39120>{number = 3, name = (null)} 2016-03-05 00:26:01.804 NSOperation-Test[660:12845] ------I am op3 in queue3------<NSThread: 0x79f39120>{number = 3, name = (null)} 2016-03-05 00:26:04.772 NSOperation-Test[660:12847] ------I am op1 in queue1------<NSThread: 0x7b217900>{number = 4, name = (null)} 2016-03-05 00:26:04.773 NSOperation-Test[660:12847] ------I am op2 in queue2------<NSThread: 0x7b217900>{number = 4, name = (null)} 2016-03-05 00:26:04.773 NSOperation-Test[660:12847] ------I am op3 in queue3------<NSThread: 0x7b217900>{number = 4, name = (null)} 2016-03-05 00:26:05.844 NSOperation-Test[660:12845] ------I am op1 in queue1------<NSThread: 0x79f39120>{number = 3, name = (null)} 2016-03-05 00:26:05.844 NSOperation-Test[660:12845] ------I am op2 in queue2------<NSThread: 0x79f39120>{number = 3, name = (null)} 2016-03-05 00:26:05.845 NSOperation-Test[660:12845] ------I am op3 in queue3------<NSThread: 0x79f39120>{number = 3, name = (null)} 2016-03-05 00:26:06.657 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:06.658 NSOperation-Test[660:12848] ------I am op2 in queue2------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:06.658 NSOperation-Test[660:12848] ------I am op3 in queue3------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:07.608 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:07.608 NSOperation-Test[660:12848] ------I am op2 in queue2------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:07.609 NSOperation-Test[660:12848] ------I am op3 in queue3------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:08.200 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:08.202 NSOperation-Test[660:12848] ------I am op2 in queue2------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:08.202 NSOperation-Test[660:12848] ------I am op3 in queue3------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:08.566 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:08.568 NSOperation-Test[660:12845] ------I am op2 in queue2------<NSThread: 0x79f39120>{number = 3, name = (null)} 2016-03-05 00:26:08.568 NSOperation-Test[660:12845] ------I am op3 in queue3------<NSThread: 0x79f39120>{number = 3, name = (null)} 2016-03-05 00:26:08.875 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:08.876 NSOperation-Test[660:12848] ------I am op2 in queue2------<NSThread: 0x79f3b4b0>{number = 2, name = (null)} 2016-03-05 00:26:08.877 NSOperation-Test[660:12848] ------I am op3 in queue3------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
可以看到即使在不同的队列中操作依然按照设定的顺序执行。
-树形依赖
// MARK:操作间依赖-树形依赖 - (void)test_dependstree { //1. 建立第一个队列 NSOperationQueue *queue1 = [[NSOperationQueue alloc]init]; NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"------I am op1 in queue1------%@", [NSThread currentThread]); }]; //2. 建立第二个队列 NSOperationQueue *queue2 = [[NSOperationQueue alloc] init]; NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"------I am op2 in queue2------%@", [NSThread currentThread]); }]; //3. 建立第二个队列 NSOperationQueue *queue3 = [[NSOperationQueue alloc] init]; NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"------I am op3 in queue3------%@", [NSThread currentThread]); }]; // 设定依赖关系 [op3 addDependency:op2]; [op3 addDependency:op1]; // 一旦放入队列则将执行 [queue1 addOperation:op1]; [queue2 addOperation:op2]; [queue3 addOperation:op3]; }
结果:
2016-03-05 00:41:47.679 NSOperation-Test[712:16784] ------I am op2 in queue2------<NSThread: 0x78e2d160>{number = 3, name = (null)} 2016-03-05 00:41:47.679 NSOperation-Test[712:16785] ------I am op1 in queue1------<NSThread: 0x78e2bd70>{number = 2, name = (null)} 2016-03-05 00:41:47.680 NSOperation-Test[712:16784] ------I am op3 in queue3------<NSThread: 0x78e2d160>{number = 3, name = (null)} 2016-03-05 00:41:49.702 NSOperation-Test[712:16787] ------I am op1 in queue1------<NSThread: 0x79999660>{number = 4, name = (null)} 2016-03-05 00:41:49.703 NSOperation-Test[712:16785] ------I am op2 in queue2------<NSThread: 0x78e2bd70>{number = 2, name = (null)} 2016-03-05 00:41:49.703 NSOperation-Test[712:16785] ------I am op3 in queue3------<NSThread: 0x78e2bd70>{number = 2, name = (null)} 2016-03-05 00:41:50.165 NSOperation-Test[712:16787] ------I am op1 in queue1------<NSThread: 0x79999660>{number = 4, name = (null)} 2016-03-05 00:41:50.165 NSOperation-Test[712:16785] ------I am op2 in queue2------<NSThread: 0x78e2bd70>{number = 2, name = (null)} 2016-03-05 00:41:50.166 NSOperation-Test[712:16785] ------I am op3 in queue3------<NSThread: 0x78e2bd70>{number = 2, name = (null)}
可以看到op3需要等待op1和op2执行完毕后才会去执行。
-思考:可以先放队列在设定依赖吗?
// MARK:操作间依赖-先放队列后设定依赖 - (void)test_dependAfterQueued { //1. 建立第一个队列 NSOperationQueue *queue1 = [[NSOperationQueue alloc]init]; NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"------I am op1 in queue1------%@", [NSThread currentThread]); }]; //2. 建立第二个队列 NSOperationQueue *queue2 = [[NSOperationQueue alloc] init]; NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"------I am op2 in queue2------%@", [NSThread currentThread]); }]; //3. 建立第二个队列 NSOperationQueue *queue3 = [[NSOperationQueue alloc] init]; NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"------I am op3 in queue3------%@", [NSThread currentThread]); }]; // 一旦放入队列则将执行 [queue1 addOperation:op1]; [queue2 addOperation:op2]; [queue3 addOperation:op3]; // 设定依赖关系 [op3 addDependency:op2]; [op3 addDependency:op1]; }
结果:
2016-03-05 00:46:40.158 NSOperation-Test[747:18522] ------I am op2 in queue2------<NSThread: 0x7a66f630>{number = 2, name = (null)} 2016-03-05 00:46:40.158 NSOperation-Test[747:18521] ------I am op1 in queue1------<NSThread: 0x7b122120>{number = 4, name = (null)} 2016-03-05 00:46:40.158 NSOperation-Test[747:18523] ------I am op3 in queue3------<NSThread: 0x7b227d60>{number = 3, name = (null)} 2016-03-05 00:46:44.109 NSOperation-Test[747:18521] ------I am op3 in queue3------<NSThread: 0x7b122120>{number = 4, name = (null)} 2016-03-05 00:46:44.109 NSOperation-Test[747:18523] ------I am op1 in queue1------<NSThread: 0x7b227d60>{number = 3, name = (null)} 2016-03-05 00:46:44.109 NSOperation-Test[747:18585] ------I am op2 in queue2------<NSThread: 0x7b122600>{number = 5, name = (null)} 2016-03-05 00:46:45.555 NSOperation-Test[747:18521] ------I am op2 in queue2------<NSThread: 0x7b122120>{number = 4, name = (null)} 2016-03-05 00:46:45.555 NSOperation-Test[747:18523] ------I am op1 in queue1------<NSThread: 0x7b227d60>{number = 3, name = (null)} 2016-03-05 00:46:45.556 NSOperation-Test[747:18522] ------I am op3 in queue3------<NSThread: 0x7a66f630>{number = 2, name = (null)} 2016-03-05 00:46:47.417 NSOperation-Test[747:18522] ------I am op1 in queue1------<NSThread: 0x7a66f630>{number = 2, name = (null)} 2016-03-05 00:46:47.417 NSOperation-Test[747:18523] ------I am op2 in queue2------<NSThread: 0x7b227d60>{number = 3, name = (null)} 2016-03-05 00:46:47.417 NSOperation-Test[747:18585] ------I am op3 in queue3------<NSThread: 0x7b122600>{number = 5, name = (null)} 2016-03-05 00:46:49.218 NSOperation-Test[747:18522] ------I am op1 in queue1------<NSThread: 0x7a66f630>{number = 2, name = (null)} 2016-03-05 00:46:49.219 NSOperation-Test[747:18523] ------I am op3 in queue3------<NSThread: 0x7b227d60>{number = 3, name = (null)} 2016-03-05 00:46:49.218 NSOperation-Test[747:18585] ------I am op2 in queue2------<NSThread: 0x7b122600>{number = 5, name = (null)}
可以看到并没有按照依赖关系的顺序执行,所以说关系必须在操作放入队列之前需要提前设定。