多线程开发2 -GCD
来源:互联网 发布:网络虚拟结婚 编辑:程序博客网 时间:2024/06/06 20:17
GCD
1.简介
全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”,基于C语言开发的多线程并发的面向过程技术;GCD是苹果为多核的并行运算提出的解决方案,使它对于多核运算更加有效。可以自动管理线程的生命周期;抽象层次更高,我们只需告诉它做什么,不需要编写多余的线程管理代码;
2.核心概念
用法: 将任务添加到队列,并且指定执行任务的函数 即可;
- 任务:执行什么操作 (block封装的内容);
- 队列:用来存放任务 (先进先出顺序);
- 执行函数: 同步执行dispatch_sync ; 异步执行dispatch_async ;
线程池概念:
实现原理: 线程池在开启线程之后永不销毁,当需要让子线程执行新的方法(使用performSelector让指定的方法在指定的子线程上运行),此线程执行任务完毕后,会放到线程池存放很短时间,如果有新任务,系统会优先调用池中的线程执行,如果没有,很快自动销毁;
3. 队列
队列类型:
- 串行队列:只有一个线程,一个任务执行完毕,再执行下一个
- 并发队列:可以有多个线程,以 先进先出 的方式,并发 调度队列中的任务 去执行
- GCD中还有一个特殊队列就是主队列,用来执行主线程上的操作任务
3.1 串行队列
通常只用串行异步组合,用于有序执行耗时的操作;
//创建队列dispatch_queue_t queue = dispatch_queue_create("name",DISPATCH_QUEUE_SERIAL); // 宏定义 为NULL;所以直接可以用NULL做参数;for (int i=0; i<10;i++) { dispatch_async(queue,^{ xxxxxxxxxx }); //异步,会开启一个线程,有序执行任务 //dispatch_sync(queue,^{ xxxxxxxxxx });//同步,不会开启新线程,在主线程上有序执行;}
注意:无论队列中所指定的执行任务的函数是同步还是异步,都必须等待前一个任务执行完毕,才可以调度后面的任务
3.2 并行队列
创建使用DISPATCH_QUEUE_CONCURRENT参数.实际开发中我们通常不会重新创建一个并发队列而是使用dispatch_get_global_queue(0,0)方法取得一个全局的并发队列(当然如果有多个并发队列可以使用前者创建;)
注意:
如果当前调度的任务是 同步 执行的,不开线程顺序执行;所以不常使用
如果当前调度的任务是 异步 执行的,开多条线程,取决于底层线程池.乱序执行
全局队列与普通并行的简单区别:
- 全局唯一,没有名字.
- 全局无论 MRC & ARC 都不需要考虑内存释放,并行在MRC下需要手动dispatch_release(q)
- dispatch_barrier阻塞 必须使用自定义的并发队列;另外,开发第3方框架时,建议使用并发队列
- 全局队列的参数:第一个long identifier:表示服务质量:枚举(iOS8之后)
- 用户交互(希望最快完成-不能用太耗时的操作)
- 用户期望(希望快,也不能太耗时)
- 默认(用来底层重置队列使用的,不是给程序员用的)
- 实用工具(专门用来处理耗时操作!)
第二个参数是为未来使用保留的,始终传入0
- 所以iOS7和iOS8适配只能使用 0,0参数dispatch_get_global_queue(0, 0);
3.3 主队列
以 先进先出 的方式, 只有在主线程空闲时 才会调度队列中的任务,只在主线程执行;
用dispatch_queue_t queue = dispatch_get_main_queue();获取
对主队列而言,不管是同步还是异步,都不会开线程
注意:
如果主线程当前有任务正在执行,那么无论主队列中当前被添加了什么任务,都不会被调度.只有当主线程空闲了,主队列才会调度任务到主线程上执行 .(所以如果调用同步主队列,会造成死锁;只有在子线程中才能这样调用:)
//子线程中dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 1. 获取主队列 dispatch_queue_t q = dispatch_get_main_queue(); // 2. 将任务添加到主队列, 并且指定同步执行 // 死锁 for (int i = 0; i < 10; i++) { dispatch_sync(q, ^{ NSLog(@"%@ %d", [NSThread currentThread], i); }); }});
队列总结:
- 开不开线程,由执行任务的函数决定;只有异步才开新线程;
- 而在异步执行任务,开几条线程由队列决定:串行队列, 只会开一条线程;并发队列, 可以开多条线程,具体开几条由线程池决定.
4.解决三大问题方式
4.1 解决耗时操作的线程阻塞.
耗时操作异步执行,回主线程中更新UI
dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"hello %@", [NSThread currentThread]); dispatch_sync(dispatch_get_main_queue(), ^{ //回主线程更新UI控件 imageView.image = image; });});
4.2 解决线程依赖-线程同步
1.直接按序
dispatch_async(dispatch_get_global_queue(0, 0), ^{//并行异步(开新线程) -无序执行 dispatch_sync(dispatch_get_global_queue(0, 0), ^{//并行同步 NSLog(@"输入密码 %@",[NSThread currentThread]); }); dispatch_sync(dispatch_get_global_queue(0, 0), ^{ NSLog(@"扣费 %@",[NSThread currentThread]); }); dispatch_async(dispatch_get_global_queue(0, 0), ^{//并行异步 NSLog(@"下载应用 %@",[NSThread currentThread]); });});
2.使用Barrier 注意:阻塞主要作用不是线程依赖,而是在多个异步操作之后,统一对非线程安全对象进行更新且不能用全局队列
//创建并发队列.dispatch_queue_t queue = dispatch_queue_create("hm", DISPATCH_QUEUE_CONCURRENT);for (int i = 0 ; i<10; i++) { dispatch_async(queue,^{ for (int i = 0 ; i<10; i++) { dispatch_barrier_async(queue,^{ NSLog(@"保存图片"); }); } NSLog(@"下载图片"); }) }//所有图片下载完毕,再执行保存图片;
3.调度组.可以做到阻塞效果,不过调度组主要是用于在多个异步操作之后;可以转到别的线程(主线程)提示用户
//1.获取队列dispatch_queue_t queue = dispatch_get_global_queue(0,0);//2.创建调度组dispatch_group_t group = dispatch_group_create();//3.添加任务 到 队列dispatch_group_async(group,queue,^{ NSLog(@"歌曲1");});dispatch_group_async(group,queue,^{ NSLog(@"歌曲2");});dispatch_group_async(group,queue,^{ NSLog(@"歌曲3");});//4所有任务都异步执行完成后,获得通知;dispatch_group_notify(group,queue,^{ //最后执行. queue可以换成main_queue,更新UI,通知用户等});
4.3 解决资源抢夺–(线程是不安全的)
//同NSThread 加互斥锁@synchronized (self) { name=[_imageNames lastObject]; [_imageNames removeObject:name];}
5. CGD的其他应用
5.1 延迟执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2*NSEC_PER_SEC)),dispatch_get_main_queue(),^{ --------- });//参数1 dispatch_time () 延迟时间//参数2 任务添加到哪个队列//参数3 要执行的block任务
5.2 一次执行(程序中只执行一次)
原理:在当前线程上执行,判断静态全局变量值, 默认0,如果执行完成后,设为1;once内部会对这个值进行判断.
static dispatch_once_t onceToken;dispatch_once(&onceToken,^{ NSLog(@"只执行一次");});
一般用来定义单例;
+(instancetype)sharexxxxOnce { static id instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken,^{ if (instance == nil) { instance = [self alloc] init]; } }); return instance;}
注意:如果要严谨的话,还要重新allocWithzone方法和copyWithzone方法;
+ (instancetype)sharedNetworkToolsOnce { return [[self alloc] init];}- (id)copyWithZone:(nullable NSZone *)zone{ return self;}+ (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super allocWithZone:zone]; }); return instance;}
5.3调度组原理
//1.获取队列dispatch_queue_t queue = dispatch_get_global_queue(0,0);//2.创建调度组dispatch_group_t group = dispatch_group_create();//ARC中不用写//dispatch_retain(group); //3 进入调度组,执行此函数后,再添加的异步 执行的block都会被group监听dispatch_group_enter(group);//4 添加任务dispatch_async(queue, ^{NSLog(@"!!--!!");dispatch_group_leave(group);//ARC中不用写dispatch_release(group);});dispatch_group_enter(group);dispatch_async(queue, ^{NSLog(@"!!------!!");dispatch_group_leave(group);});//5 获得调度组的通知dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"okkkkkkk %@",[NSThread currentThread]);});//6 等待调度组 监听的队列中的所有任务全部执行完毕,才会执行后续代码,会阻塞线程(很少使用)
- 多线程开发2 -GCD
- iOS开发 多线程GCD
- iOS多线程开发 -------- GCD
- ios 多线程开发 GCD
- iOS多线程开发:GCD
- 转载--- GCD多线程开发
- GCD多线程开发
- iOS开发,多线程GCD
- iosiOS开发多线程GCD
- iOS GCD多线程开发
- IOS多线程开发之GCD
- IOS多线程开发之GCD
- IOS多线程开发之GCD
- IOS多线程开发之GCD
- IOS多线程开发之GCD
- IOS多线程开发之GCD
- iOS多线程开发之GCD
- iOS 开发多线程 - GCD
- uva348 区间DP
- 【模板】【笔记】字符串相关
- cin cout里面的c是哪个单词的首字母?
- SpringMVC整合Shiro
- 笔记:Centos 6安装过程
- 多线程开发2 -GCD
- 图像简介
- 对于好书APP的总结
- hdu 2844 Coins
- Web Service有关术语的解释
- kmp算法
- 判断其是否为一个回文串,java实现
- c++中使用struct,struct中有指针
- 用java代码实现二叉树的遍历算法