一、GCD简介
-
什么是GCD?
全称是 Grand Central Dispatch
纯 C 语言,提供了非常多强大的函数
-
GCD的优势
GCD 是苹果公司为多核的并行运算提出的解决方案
GCD 会自动利用更多的CPU内核(比如双核、四核)
GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD 想要执行什么任务,不需要编写任何线程管理代码
-
劣势
自动控制生命周期,程序员不能自由操作
-
函数
- (void)syncTest { // 把任务添加到队列 --> 函数 // 任务 ( _t ref 都是 c对象) dispatch_block_t block = ^{ NSLog(@"hello GCD"); }; //串行队列 <- 因为第二个参数是NULL dispatch_queue_t queue = dispatch_queue_create("com.hehe.cn", NULL); // 函数 dispatch_async(queue, block); }
- 任务使用 block 封装
- 任务的 block 没有参数也没有返回值
- 执行任务的函数
- 异步
dispatch_async
- 不用等待当前语句执行完毕,就可以执行下一条语句
- 会开启线程执行 block的任务
- 异步是多线程的代名词
- 同步
dispatch_sync
- 必须等待当前语句执行完毕,才会执行下一条语句
- 不会开启线程
- 在当前执行 block的任务
- 异步
-
队列
串行队列 - DISPATCH_QUEUE_SERIAL: 只能一个一个任务挨个出去
并行队列 - DISPATCH_QUEUE_CONCURRENT: 多个任务同时出去
-
队列和函数的组合
-
同步函数串行队列:
1、不会开启线程,在当前线程执行任务
2、任务串行执行,任务一个接着一个
3、会产生堵塞
-
同步函数并发队列:
1、不会开启线程,在当前线程执行任务
2、任务一个接着一个
-
异步函数串行队列:
1、开启线程一条新线程
2、任务一个接着一个
-
异步函数并发队列:
1、开启线程,在当前线程执行任务
2、任务异步执行,没有顺序,CPU调度有关
- (void)testDemo { dispatch_queue_t queue = dispatch_queue_create("hehe",DISPATCH_QUEUE_CONCURRENT); NSLog(@"1"); // 耗时 dispatch_async(queue, ^{ NSLog(@"2"); dispatch_async(queue, ^{ NSLog(@"3"); }); NSLog(@"4"); }); NSLog(@"5"); // 打印: 1 5 2 4 3 // 因为dispatch_async不堵塞外部线程 // 假设:一个通道代表串行,多个通道代表并发,一辆车代表一个线程,每个通道有每辆车的出货的顺序 // 个人理解: // 车a:拉1,拉异步代码块:“我有车你先走”,拉5,其中异步代码块产生车b // 车b:拉2,拉异步代码块:“我有车你先走”,拉4,其中异步代码块产生车c // 车c:拉3 // // // }
- (void)testDemo2 { // 串行队列 dispatch_queue_t queue = dispatch_queue_create("hehe",DISPATCH_QUEUE_SERIAL); NSLog(@"1"); // 异步函数 dispatch_async(queue, ^{ NSLog(@"2"); // 同步 dispatch_sync(queue, ^{ //NSLog(@"3"); }); // 这个地方没有代码一样会产生死锁 //NSLog(@"4"); }); NSLog(@"5"); // 152 // 个人理解: // 串行队列说明只有一条通道,异步函数会创建多辆车,通道有每辆车的出货的顺序 // 以下是计划: // 1、车a拉1,车a拉异步代码块,异步代码块说:“你先走,我自己有车”,车a拉了5走了 // 2、异步代码块中创建车b // 3、车b拉2,车b拉同步代码块,车b拉4 // 实际情况: // 车b在拉同步代码块的时候被告知:“我没车,你得拉3,不然你啥也别干了” // 车b:“通道要求我得先把4拉了” // 于是: // 产生死锁,3和4谁也不拉 }
- (void)testDemo1 { dispatch_queue_t queue = dispatch_queue_create("hehe",DISPATCH_QUEUE_CONCURRENT); NSLog(@"1"); dispatch_async(queue, ^{ NSLog(@"2"); dispatch_sync(queue, ^{ NSLog(@"3"); }); NSLog(@"4"); }); NSLog(@"5"); //打印 :1 5 2 3 4 // 个人理解: // 并行队列说明有多条通道,同步函数会创建一辆车,每个通道有出货的顺序 // 以下是计划: // 1、车a拉1,车a拉异步代码块,异步代码块说:“你先走,我自己有车”,车a拉了5走了 // 2、异步代码块中创建车b // 3、车b拉2,车b拉同步代码块,车b拉4 // 4、同步代码块说:“你得先拉3,不然你啥也别干了” // 5、车b说:“当前通道要求我先拉4,我换个通道把3拉了再回来拉4” }
-
-
死锁
- 主线程因为你同步函数的原因等着先执行任务
- 主队列等着主线程的任务执行完毕再执行自己的任务
- 主队列和主线程相互等待会造成死锁
-
特殊队列
系统加载时候的默认队列:
-
dispatch_get_global_queue(0, 0);
全局队列:并发队列
不建议用
- 为了方便程序员的使用,苹果提供了全局队列
- 在使用多线程开发时,如果对队列没有特殊需求,在执行异步任务时,可以直接使用全局队列
-
dispatch_get_main_queue();
主队列:串行队列
- 专门用来在主线程上调度任务的队列
- 不会开启线程
- 如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度
int a = 0; while (a<10) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ a++; }); } //问题1: // a会报错,为什么? // //问题2: // a会输出什么值? // >=10 // 原因:资源抢夺 // 创建很多条线程,都会操作a //问题3: // 输出a最后的值该怎么修改? // __block int a = 0; //问题1修复 //把a从栈区copy成一个struct(中有a的指针地址和a的值) //block中会修改newA while (a<10) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ a++; }); } //问题3 //最后 // 主线程 -- 堵塞 -- I/O -- 代码执行的耗时 -- 相当于一个同步效果 // 堵塞 -- 给你足够的时间 -- 把前面的任务执行完毕之后 -- 往队列加入任务 -- 再执行 NSLog(@"主线程%d",a);//此时打印的也不是真的值 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"最后%d",a); });
-
二、GCD的应用
-
栅栏函数
-
一定要是自定义的并发队列,不然没有效果
如果栅栏函数堵塞了全局并发队列那系统就GG了
-
dispatch_barrier_async和dispatch_barrier_sync
- dispatch_barrier_async 前面的任务执行完毕才会来到这里,不影响队列外任务的执行
- dispatch_barrier_sync 作用相同,但是这个会堵塞线程,影响后面的任务执行,包括队列外的任务
-
栅栏函数只能控制同一并发队列 --- 不够优秀的地方 --- 不利于封装
-
-
调度组
控制任务执行顺序
-
dispatch_group_create 创建组
dispatch_group_async 进组任务
dispatch_group_notify 进组任务执行完毕通知
dispatch_group_wait 进组任务执行等待时间
-
dispatch_group_enter 进组
dispatch_group_leave 出组
注意搭配使用
-
-
信号量dispatch_semaphore_t
-
Dispatch_Source
网友评论