GCD
2016-03-19 17:23
435 查看
<span style="font-family: 'Helvetica Neue'; background-color: rgb(255, 255, 255);">GCD(Grand Central Dispatch)——iOS 4引入的技术</span>
1.线程的一些基础知识
进程:一个正在运行的程序可以看做一个进程。
线程:程序中独立运行的代码段。
一个进程是由至少一个的线程组成,进程只负责资源的调度和分配,线程才是程序真正的执行单元。
在单核 CPU 时代,CPU只能处理1条线程,只有1条线程在工作
多线程并发执行,其实是CPU快速地在多条线程之间调度(切换)
如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象来实现软件层面的多线程。创建线程,线程间切换都是有成本开销的。但由于多线程可以避免阻塞所造成的
CPU 计算时间浪费,所以多线程所带来的开销成本总体看来是值得的。任务一般都可以被拆分成多个子任务,如果一个子任务发生了阻塞,计算时间就可以分配给其他子任务。这样就提高了 CPU 的利用率。 而基于XNU内核的iOS在发生操作系统时间时会切换执行路径,将CPU的寄存器等信息保存在专用的内存块中,复原时再从内存块中复原CPU的寄存器等信息信息,继续执行CPU指令。
但是现在的物理CPU芯片一般都是有64个(64核)cpu。由于硬件上就支持多线程技术,就可以让多个线程真正同时地运行。从而真的提供了多个CPU核并行执行多个线程的技术。
多线程的目的是,通过并发执行提高 CPU 的使用效率,进而提供程序运行效率。
2.iOS中的多线程
而iOS中的多线程API有不少,大体有下面几个
NSOperation(基于GCD的OC封装,对于初学者来说接口更友好,性能比GCD差点)
NSThread(用的比较少,一般也只会做一些简单的逻辑,)
GCD(iOS中多线程的绝对主力,简单方便功能强大,也是这篇文章说的重点)
3.GCD
1.Dispatch Queue (执行处理的等待队列):Dispatch Queue
的执行顺序是FIFO,包含两种
1>Serial Dispatch Queue -串行(等待现在执行中处理结束)
2>Concurrent Dispatch Queue -并行(不等待现在执行中的处理结束)
4.GCD具体方法
1>创建dispatch_queue_create
dispatch_queue_t OneserialDispatchQueue = dispatch_queue_create(NULL, NULL);这是创建一个标示为空的串行队列。创建具体的队列和实现场景直接上代码
//Serial Dispatch Queue的使用场景一般是在处理数据竞争类似的问题 // Serial Dispatch Queue 虽然是串行队列,但是可以创建多个,然后将执行添加到多个Serial Dispatch Queue中,其执行方式是并发的 // 第一个参数:队列的标识,---该参数也可以是NULL,但是建议使用程序ID因为在调试和CrashLog中可以方便识别 //第二个参数:队列的类型(串行/并行) ---可以是NULL那么就是串行 NULL = DISPATCH_QUEUE_SERIAL / DISPATCH_QUEUE_CONCURRENT(并行) dispatch_queue_t OneserialDispatchQueue = dispatch_queue_create(NULL, NULL); dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT); dispatch_queue_t TwoserialDispatchQueue = dispatch_queue_create("TwoserialDispatchQueue", DISPATCH_QUEUE_SERIAL); dispatch_queue_t ThreeserialDispatchQueue = dispatch_queue_create("ThreeserialDispatchQueue", NULL); dispatch_async(OneserialDispatchQueue, ^{ sleep(2); NSLog(@"1="); }); dispatch_async(TwoserialDispatchQueue, ^{ sleep(2); NSLog(@"2="); }); dispatch_async(ThreeserialDispatchQueue, ^{ sleep(2); NSLog(@"3="); }); dispatch_async(OneserialDispatchQueue, ^{ sleep(2); NSLog(@"4="); }); dispatch_async(concurrentDispatchQueue, ^{ sleep(2); NSLog(@"5="); }); NSLog(@"%@",OneserialDispatchQueue); // 执行顺序应该是1、2、3、5三个并发先执行(1、2、3、5的执行顺序可能每次都不一样因为是并发,GCD的FIFO指的是同一个线程),4肯定晚于1执行(FIFO原则)。因为1、2、3、5(5是并发队列)是在三个不同的串行队列中然后异步加入队列,4和1是加入到同一个一个串行中,所以必然等到1执行完成后再执行 /* 在MRC和ARC的iOS6之前(这两种情况现在非常少,我是没见过适配iOS6之前的)我们要对GCD进行内存管理,使用下面的方法。 dispatch_release(OneserialDispatchQueue); dispatch_retain(OneserialDispatchQueue); */ // iOS6之后GCD已经进入ARC的管理了,我们就不用管这个了2> 获取系统的Dispatch Queue
工作中我们大多数情况下不用自己创建Dispatch Queue,我们可以方便的获取系统标准提供的Dispatch Queue那就是下面的两个
dispatch_queue_t main = dispatch_get_main_queue();
如其名指的就是主线程(更新UI必须在此线程)所以也肯定是 Serial Dispatch Queue它的处理在主线程的RunLoop中执行
还有就是dispatch_get_global_queue
/* dispatch_get_global_queue是 concurrent Dispatch Queue 第一个参数有四个优先级 #define DISPATCH_QUEUE_PRIORITY_HIGH 2 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 #define DISPATCH_QUEUE_PRIORITY_LOW (-2) #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 再向Global Dispatch Queue 中追加处理时,应选择与处理内容对应的优先级,但是XNU内核用于Global Dispatch Queue的线程并不能保证实时性,只是大致判断 */ dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // dispatch_queue_create创建的就是DISPATCH_QUEUE_PRIORITY_DEFAULT这种优先级3>变更优先级:dispatch_set_target_queue
这个一般都用不到
dispatch_queue_t serialDispatchQueue = dispatch_queue_create(NULL, NULL); dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); /* 变更生成的Dispatch Queue的执行优先级 第一个参数:要变更的Dispatch Queue 第二个参数;目标Dispatch Queue */ dispatch_set_target_queue(serialDispatchQueue, globalDispatchQueue);
4>延时执行dispatch_after
// 作用:在指定时间后执行处理 // 注意:dispatch_after并不是在指定时间后执行处理,而只是在指定时间追加处理到Dispatch Queue,如果在每隔1/60秒执行的RunLoop中,可能会在time+1/60秒后执行甚至更久。 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"dispatch_after执行"); });
5>dispatch_group_t
这个比较重要也不是很好理解,就多讲点
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 1创建dispatch_group_t dispatch_group_t group = dispatch_group_create(); /* 第一个参数:dispatch_group_t 第二个参数:要执行的block的Dispatch Queue 加入group的线程可以是任意的,主线程子线程都可以, */ dispatch_group_async(group, queue, ^{ NSLog(@"任务1"); }); dispatch_group_async(group, queue, ^{ sleep(1); NSLog(@"任务2"); }); dispatch_group_async(group, queue, ^{ sleep(2); NSLog(@"任务3"); }); dispatch_group_async(group, queue, ^{ NSLog(@"任务4"); }); // 3,在所有加到group的任务执行完成后,再执行此任务,无论上边的queue是否一致,
还是上边的Queue和dispatch_group_notify中的Queue是否一致,下边的block总是在上边的执行完成后才执行 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"最后任务"); }); NSLog(@"dispatch_group_notify是异步执行"); // 还有一种用法是将dispatch_group_notify换成dispatch_group_wait,但是dispatch_group_wait会堵塞当前线程,直到等待时间到达或者group执行完成 dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC); // 这个函数是有返回值的 #warning 最好不要轻易使用dispatch_group_wait,因为会阻塞线程,而且代码也会复杂 long result = dispatch_group_wait(group, time); if (result == 0) { NSLog(@"group中的全部处理完成"); }else{ NSLog(@"group中还有处理在进行"); } NSLog(@"dispatch_group_wait阻塞了当前线程");
dispatch_group_t手动管理group关联的block的运行状态(或计数),进入和退出group次数必须匹配
dispatch_group_enter(group);
dispatch_group_leave(group);
dispatch_group_enter(group);
一般这样实现
dispatch_group_async(group, queue, ^{ });
dispatch_async(queue, ^{
dispatch_group_leave(group);
});
这两者等价
6>dispatch_barrier_async
dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue(串行对于此函数是没有意义的)上的并行执行的处理全部结束之后,再执行dispatch_barrier_async中的block,此block执行完成后,Concurrent
Dispatch Queue的处理又开始并行执行
此函数在处理数据竞争时是非常有用的,比如读数据时不能写数据,写数据时不能读,读数据可以一块执行
dispatch
a364
_barrier_async只能和dispatch_queue_create创建的Queue一起使用,和dispatch_get_global_queue创建的函数一块使用是有问题的.
dispatch_barrier_async在 一般在自定义并发队列使用
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ sleep(2); NSLog(@"读数据1"); }); dispatch_async(queue, ^{ sleep(1); NSLog(@"读数据2"); }); dispatch_async(queue, ^{ sleep(3); NSLog(@"读数据3"); }); dispatch_barrier_async(queue, ^{ NSLog(@"写数据dispatch_barrier_async任务"); }); dispatch_async(queue, ^{ sleep(2); NSLog(@"读数据4"); }); dispatch_async(queue, ^{ sleep(1); NSLog(@"读数据5"); });
7>dispatch_sync
如其名,就是同步,就是等待当现程处理结束时才执行
我们在主线程需要更新UI,但是在数据没有处理完时不能更新,处理完时要立即更新,所以可以使用此种写法
NSLog(@"开始"); dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); NSLog(@"处理"); }); NSLog(@"完结"); #warning dispatch_sync使用时要格外注意,因为很容易造成死锁 //dispatch_sync,在指定处理没有完成时是不会返回的,下面这种就会造成死锁,原因是主线程在等待block的执行,而无法去执行block dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"死锁"); }); NSLog(@"永远不会执行");
8>dispatch_suspend
当想要让一个队列的执行暂时挂起可以调用dispatch_suspend,
dispatch_queue_t queue = dispatch_queue_create("dispatchSuspend", NULL); // 挂起指定的 Queue dispatch_suspend(queue); // 恢复指定的 Queue dispatch_resume(queue);
9>dispatch_semaphore_t信号量
主要也是解决数据竞争等问题
// 创建semaphore 参数标示计数初始值 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); // 等待semaphore 当参数计数为0时等待,为1或者大于1时,则减去1而不等待 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); /* 第二个参数也可以设置成为一段时间,当返回值是0的时候,说明在指定时间内数值达到大于等于1或者semaphore达到大于等于1 */ long result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC)); if (result == 0) { // codeing }else{ // 时间到,semaphore数值还是0 } //semaphore 数值加1 dispatch_semaphore_signal(semaphore);
10>dispatch_suspend队列挂起
dispatch_queue_t queue = dispatch_queue_create("dispatchSuspend", NULL); // 挂起指定的 Queue dispatch_suspend(queue); // 恢复指定的 Queue dispatch_resume(queue);11>dispatch_once_t
// 线程安全的只执行一次指定处理,最主要的作用就是创建单例,怎么创建应该做过iOS开发的都知道 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // code to be executed once });
总结:1.GCD是iOS多线程开发中最常用的技术,功能强大。但是也不要滥用,因为会让你和你的同事在后面的维护上增加难度。所以不要滥用
2.GCD的实现有时候并不能完全按着我们的想法执行,因为苹果会在底层进行自己的优化,比如:
dispatch_queue_t myQueue = dispatch_queue_create("com.team", NULL); dispatch_sync(myQueue, ^{ NSLog(@"task2 == %@",[NSThread currentThread]); });task2按理应该在myQueue中执行,但是它实际在main queue中执行。具体原因可以看一下这个原因。但是这并不影响我们的使用。
3.学好GCD,妈妈再也不担心我的多线程了!!!!!!!!!!!!
文中demo:点击打开
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- 讲解iOS开发中基本的定位功能实现
- iOS中定位当前位置坐标及转换为火星坐标的方法
- js判断客户端是iOS还是Android等移动终端的方法
- iOS应用中UISearchDisplayController搜索效果的用法
- IOS开发环境windows化攻略
- iOS应用中UITableView左滑自定义选项及批量删除的实现
- 浅析iOS应用开发中线程间的通信与线程安全问题
- 检测iOS设备是否越狱的方法
- .net平台推送ios消息的实现方法
- 探讨Android与iOS,我们将何去何从?
- Android、iOS和Windows Phone中的推送技术详解
- iOS推送的那些事
- IOS 改变键盘颜色代码