1.GCD
GCD全称是Grand Central Dispatch,译为"强大的中枢管理器"
1)什么是任务?什么是队列?
任务和队列是GCD的核心.
任务: 执行什么操作
队列: 用来存放任务
2)用GCD创建线程的两种方式.
01.使用并发队列创建
//01 获得并发队列 /* 第一个参数:C语言的字符串 对队列的名称(com.520it.www.DownloadQueue) 第二个参数:队列的类型 DISPATCH_QUEUE_SERIAL 串行队列 DISPATCH_QUEUE_CONCURRENT 并发队列 */ dispatch_queue_t queue = dispatch_queue_create("www.baidu.com", DISPATCH_QUEUE_CONCURRENT); //02 封装任务并把任务添加到队列 dispatch_async(queue, ^{ NSLog(@"download1---%@",[NSThread currentThread]); });
02.使用全局队列创建
//02 使用获得全局并发队列,开启子线程 /* 第一个参数:队列的优先级 第二个参数: */ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //02 封装任务并把任务添加到队列 dispatch_async(queue, ^{ NSLog(@"download1---%@",[NSThread currentThread]); });
3)同步,异步,并发,串行
同步函数: 在当前线程中执行任务,不具备开线程的能力
异步函数: 在新的线程中执行任务,具备开线程的能力
并发队列: 多个任务并发执行
串行队列: 一个任务执行完之后,再执行下一个
4)是否会开启线程
01 异步函数+并发队列:开启多条线程,并发执行任务
02 异步函数+串行队列:开启一条线程,串行执行任务
03 同步函数+并发队列:不开线程,串行执行任务
04 同步函数+串行队列:不开线程,串行执行任务
05 异步函数+主队列:不开线程,在主线程中串行执行任务
06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)
5) 为什么使用同步函数+主队列(串行队列)会发生死锁?
首先要明白主队列是在主线程中,而同步函数不具备开启子线程的能力.假设A正在主线程中执行,而在A执行的过程中又需要去执行B.由于同步函数+主队列并不会开启子线程,只能去主线程中执行,而主线程中A正在执行.由A需要C去执行,C又需要等A执行完毕,才能执行,就会造成死锁.
6) GCD中常用的函数
1)一次性代码,整个程序运行过程中只执行一次,可以用作创建单例,线程安全
static dispatch_once_t onceToken; //内部的实现原理:最开始的时候onceToken==0 如果onceToken==0 那么就执行一次,onceToken=-1 NSLog(@"%zd",onceToken); dispatch_once(&onceToken, ^{ NSLog(@"once"); });
2)延迟函数
dispatch_queue_t queue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_SERIAL); /* 延迟2秒,然后再把任务提交到队列 */ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{ NSLog(@"GCD----%@",[NSThread currentThread]); });
3)遍历函数
//快速迭代(并发队列):会开启子线程和主线程一起执行任务,所有的任务并发执行 /* 第一个参数:要遍历的次数 第二个参数:队列 ~ 线程 全局并发队列 == 自己创建的并发队列 自己创建的串行队列 == for循环 主队列:死锁 */ dispatch_queue_t queue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_apply(10, queue, ^(size_t i) { NSLog(@"%zd---%@",i,[NSThread currentThread]); });
4)栅栏函数
//需求:1)有四个任务,开子线程并发的执行这四个任务 //2)添加任务+++++++,但是要求必须要等1|2都执行完才执行++++,必须要等+++打印执行完才能执行后面的任务 //3)所有的任务都在子线程中执行(dispatch_barrier_async) //栅栏 = 篱笆 //01 获得并发队列 dispatch_queue_t queue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_CONCURRENT); //注意:!!!! 栅栏函数在使用中不能使用全局并发队列(会丧失拦截的功能) //dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //02 异步函数 dispatch_async(queue, ^{ NSLog(@"1----%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"2----%@",[NSThread currentThread]); }); //特点:拦截上面的任务必须等前面的任务执行完才执行当前的block,必须等当前的block快执行完才执行后面 //dispatch_barrier_async 子线程中执行 //dispatch_barrier_sync 当前线程 dispatch_barrier_async(queue, ^{ NSLog(@"+++++%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"3----%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"4----%@",[NSThread currentThread]); });
4:NSOperation(操作队列)
1)基本概念
NSOperation本身是抽象类,只能使用它的子.分别是NSBlockOperation、NSInvocationOperation以及自定义继承自NSOperation的类
2)操作队列的核心是: 队列 + 操作
其实它的使用也很简单,就是先创建队列,然后把你封装的操作加到队列,操作队列默认的是并发队列
3)NSInvocationOperation的使用(使用不多)
//01 创建队列 /* 操作队列: ① 自己创建(自定义)[并发队列*串行队列,默认是并发队列] [[NSOperationQueue alloc]init] ② 主队列[串行队列] [NSOperationQueue MainQueue] */ NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //02 封装操作 NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil]; NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil]; //03 把操作添加到队列 [queue addOperation:op1]; [queue addOperation:op2];
4)NSBlockOperation
1)怎么创建
//01 创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //02 封装操作 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"1----%@",[NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"2----%@",[NSThread currentThread]); }]; //03 把操作添加到队列 [queue addOperation:op1]; //addOperation 内部调用start方法 [queue addOperation:op2];
2) 设置依赖和监听(通常在下载的时候用的到)
//01 创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; NSOperationQueue *queue2 = [[NSOperationQueue alloc]init]; //02 封装操作 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"1----%@",[NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"2----%@",[NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"3----%@",[NSThread currentThread]); }]; NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"4-下载电影-%@",[NSThread currentThread]); }]; NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"+++++++5+%@+++",[NSThread currentThread]); }]; //设置监听 |当op4任务结束的时候会执行block中的代码 //completionBlock 在子线程中执行 op4.completionBlock = ^{ NSLog(@"我已经被下载完了,快点来看我吧--%@",[NSThread currentThread]); }; //设置依赖 //5-4-3-2-1 [op1 addDependency:op2]; //必须要等任务2执行完毕才能执行任务1 //[op2 addDependency:op1]; !!! 不能设置循环以来 [op2 addDependency:op3]; [op3 addDependency:op4]; [op4 addDependency:op5]; //03 把操作添加到队列 [queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3]; [queue addOperation:op4]; [queue2 addOperation:op5];
输出结果:
5)设置最大并发数
/1.创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //2.设置最大并发数 //注意点:该属性需要在任务添加到队列中之前进行设置 //该属性控制队列是串行执行还是并发执行 //如果最大并发数等于1,那么该队列是串行的,如果大于1那么是并行的 //系统的最大并发数有个默认的值,为-1,如果该属性设置为0,那么不会执行任何任务 queue.maxConcurrentOperationCount = 2;
6)暂停和恢复以及取消
//设置暂停和恢复 //suspended设置为YES表示暂停,suspended设置为NO表示恢复 //暂停表示不继续执行队列中的下一个任务,暂停操作是可以恢复的 if (self.queue.isSuspended) { self.queue.suspended = NO; }else { self.queue.suspended = YES; } //取消队列里面的所有操作 //取消之后,当前正在执行的操作的下一个操作将不再执行,而且永远都不在执行,就像后面的所有任务都从队列里面移除了一样 //取消操作是不可以恢复的 [self.queue cancelAllOperations];
最后关于GCD 和 NSOperation的对比
1)GCD是纯C语言的API,而操作队列则是Object-C的对象。 2)在GCD中,任务用块(block)来表示,而块是个轻量级的数据结构;相反操作队列中的『操作』NSOperation则是个更加重量级的Object-C对象。 3)具体该使用GCD还是使用NSOperation需要看具体的情况 NSOperation和NSOperationQueue相对GCD的好处有: 1)NSOperationQueue可以方便的调用cancel方法来取消某个操作,而GCD中的任务是无法被取消的(安排好任务之后就不管了)。 2)NSOperation可以方便的指定操作间的依赖关系。 3)NSOperation可以通过KVO提供对NSOperation对象的精细控制(如监听当前操作是否被取消或是否已经完成等) 4)NSOperation可以方便的指定操作优先级。操作优先级表示此操作与队列中其它操作之间的优先关系,优先级高的操作先执行,优先级低的后执行。 5)通过自定义NSOperation的子类可以实现操作重用,
附:
最近拜读了文顶顶大牛的博客,确实牛,强烈推荐......