iOS开发之GCD使用总结

GCD是iOS的一种底层多线程机制,今天总结一下GCD的常用API和概念,希望对大家的学习起到帮助作用。

GCD队列的概念

在多线程开发当中,程序员只要将想做的事情定义好,并追加到DispatchQueue(派发队列)当中就好了。

派发队列分为两种,一种是串行队列(SerialDispatchQueue),一种是并行队列(ConcurrentDispatchQueue)。

一个任务就是一个block,比如,将任务添加到队列中的代码是:

1 dispatch_async(queue, block);

当给queue添加多个任务时,如果queue是串行队列,则它们按顺序一个个执行,同时处理的任务只有一个。

当queue是并行队列时,不论第一个任务是否结束,都会立刻开始执行后面的任务,也就是可以同时执行多个任务。

但是并行执行的任务数量取决于XNU内核,是不可控的。比如,如果同时执行10个任务,那么10个任务并不是开启10个线程,线程会根据任务执行情况复用,由系统控制。

获取队列

系统提供了两个队列,一个是MainDispatchQueue,一个是GlobalDispatchQueue。

前者会将任务插入主线程的RunLoop当中去执行,所以显然是个串行队列,我们可以使用它来更新UI。

后者则是一个全局的并行队列,有高、默认、低和后台4个优先级。

它们的获取方式如下:

1 dispatch_queue_t queue = dispatch_get_main_queue(); 2
3 dispatch queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRORITY_DEFAULT, 0)

执行异步任务

1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 2     dispatch_async(queue, ^{ 3         //...
4     });

这个代码片段直接在子线程里执行了一个任务块。使用GCD方式任务是立即开始执行的

它不像操作队列那样可以手动启动,同样,缺点也是它的不可控性。

令任务只执行一次

1 + (id)shareInstance { 2     static dispatch_once_t onceToken; 3     dispatch_once(&onceToken, ^{ 4         _shareInstance = [[self alloc] init]; 5     }); 6 }

这种只执行一次且线程安全的方式经常出现在单例构造器当中。

任务组

有时候,我们希望多个任务同时(在多个线程里)执行,再他们都完成之后,再执行其他的任务,

于是可以建立一个分组,让多个任务形成一个组,下面的代码在组中多个任务都执行完毕之后再执行后续的任务:

 1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  2     dispatch_group_t group = dispatch_group_create();  3
 4     dispatch_group_async(group, queue, ^{ NSLog(@"1"); });  5     dispatch_group_async(group, queue, ^{ NSLog(@"2"); });  6     dispatch_group_async(group, queue, ^{ NSLog(@"3"); });  7     dispatch_group_async(group, queue, ^{ NSLog(@"4"); });  8     dispatch_group_async(group, queue, ^{ NSLog(@"5"); });  9
10     dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"done"); });

延迟执行任务

1     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 2         //... 3     });

这段代码将会在10秒后将任务插入RunLoop当中。

dispatch_asycn和dispatch_sync

先前已经有过一个使用dispatch_async执行异步任务的一个例子,下面来看一段代码:

1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 2
3     dispatch_async(queue, ^{ 4         NSLog(@"1"); 5     }); 6
7     NSLog(@"2");

这段代码首先获取了全局队列,也就是说,dispatch_async当中的任务被丢到了另一个线程里去执行,async在这里的含义是,当当前线程给子线程分配了block当中的任务之后,当前线程会立即执行,并不会发生阻塞,也就是异步的。那么,输出结果不是12就是21,因为我们没法把控两个线程RunLoop里到底是怎么执行的。

类似的,还有一个“同步”方法dispatch_sync,代码如下:

1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 2
3     dispatch_sync(queue, ^{ 4         NSLog(@"1"); 5     }); 6
7     NSLog(@"2");

这就意味着,当主线程将任务分给子线程后,主线程会等待子线程执行完毕,再继续执行自身的内容,那么结果显然就是12了。

需要注意的一点是,这里用的是全局队列,那如果把dispatch_sync的队列换成主线程队列会怎么样呢:

1     dispatch_queue_t queue = dispatch_get_main_queue(); 2     dispatch_sync(queue, ^{ 3         NSLog(@"1"); 4     });

这段代码会发生死锁,因为:

1.主线程通过dispatch_sync把block交给主队列后,会等待block里的任务结束再往下走自身的任务,

2.而队列是先进先出的,block里的任务也在等待主队列当中排在它之前的任务都执行完了再走自己。

这种循环等待就形成了死锁。所以在主线程当中使用dispatch_sync将任务加到主队列是不可取的。

创建队列

我们可以使用系统提供的函数获取主串行队列和全局并行队列,当然也可以自己手动创建串行和并行队列,代码为:

1     dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.Steak.GCD", DISPATCH_QUEUE_SERIAL); 2     dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.Steak.GCD", DISPATCH_QUEUE_CONCURRENT);

在MRC下,手动创建的队列是需要释放的

1     dispatch_release(myConcurrentDispatchQueue);

手动创建的队列和默认优先级全局队列优先级等同,如果需要修改队列的优先级,需要:

1     dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.Steak.GCD", DISPATCH_QUEUE_CONCURRENT); 2     dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); 3     dispatch_set_target_queue(myConcurrentDispatchQueue, targetQueue);

上面的代码修改队列的优先级为后台级别,即与默认的后台优先级的全局队列等同。

串行、并行队列与读写安全性

在向串行队列(SerialDispatchQueue)当中加入多个block任务后,一次只能同时执行一个block,如果生成了n个串行队列,并且向每个队列当中都添加了任务,那么系统就会启动n个线程来同时执行这些任务。

对于串行队列,正确的使用时机,是在需要解决数据/文件竞争问题时使用它。比如,我们可以令多个任务同时访问一块数据,这样会出现冲突,也可以把每个操作都加入到一个串行队列当中,因为串行队列一次只能执行一个线程的任务,所以不会出现冲突。

但是考虑到串行队列会因为上下文切换而拖慢系统性能,所以我们还是很期望采用并行队列的,来看下面的示例代码:

 1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  2     dispatch_async(queue, ^{  3         //数据读取  4     });  5     dispatch_async(queue, ^{  6         //数据读取2  7     });  8     dispatch_async(queue, ^{  9         //数据写入 10     }); 11     dispatch_async(queue, ^{ 12         //数据读取3 13     }); 14     dispatch_async(queue, ^{ 15         //数据读取4 16     });

显然,这5个操作的执行顺序是我们无法预期的,我们希望在读取1和读取2执行结束后,再执行写入,写入完成后再执行读取3和读取4。

为了实现这个效果,这里可以使用GCD的另一个API:

1     dispatch_barrier_async(queue, ^{ 2         //数据写入 3     });

这样就保证的写入操作的并发安全性。

对于没有数据竞争的并行操作,则可以使用并行队列(CONCURRENT)来实现。

JOIN行为

CGD利用dispatch_group_wait来实现多个操作的join行为,代码如下:

 1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  2     dispatch_group_t group = dispatch_group_create();  3       4     dispatch_group_async(group, queue, ^{  5         sleep(0.5);  6         NSLog(@"1");  7     });  8     dispatch_group_async(group, queue, ^{  9         sleep(1.5); 10         NSLog(@"2"); 11     }); 12     dispatch_group_async(group, queue, ^{ 13         sleep(2.5); 14         NSLog(@"3"); 15     }); 16  17     NSLog(@"aaaaa"); 18      19     dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2ull * NSEC_PER_SEC); 20     if (dispatch_group_wait(group, time) == 0) { 21         NSLog(@"已经全部执行完毕"); 22     } 23     else { 24         NSLog(@"没有执行完毕"); 25     } 26      27     NSLog(@"bbbbb");

这里起了3个异步线程放在一个组里,之后通过dispatch_time_t创建了一个超时时间(2秒),程序之后行,立即输出了aaaaa,这是主线程输出的,当遇到dispatch_group_wait时,主线程会被挂起,等待2秒,在等待的过程当中,子线程分别输出了1和2,2秒时间达到后,主线程发现组里的任务并没有全部结束,然后输出了bbbbb。

在这里,如果超时时间设置得比较长(比如5秒),那么会在2.5秒时第三个任务结束后,立即输出bbbbb,也就是说,当组中的任务全部执行完毕时,主线程就不再被阻塞了。

如果希望永久等待下去,时间可以设置为DISPATCH_TIME_FOREVER。

并行循环

类似于C#的PLINQ,OC也可以让循环并行执行,在GCD当中有一个dispatch_apply函数:

1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 2     dispatch_apply(20, queue, ^(size_t i) { 3         NSLog(@"%lu", i); 4     });

这段代码让i并行循环了20次,如果内部处理的是一个数组,就可以实现对数组的并行循环了,它的内部是dispatch_sync的同步操作,所以在执行这个循环的过程当中,当前线程会被阻塞。

暂停和恢复

使用dispatch_suspend(queue)可以暂停队列中任务的执行,使用dispatch_result(queue)可以继续执行被暂停的队列。

  都说程序员的工资高,却很少了解他们加班的痛苦,你是不是每次也在心里想,按时间折算下来这个工资都给少了,于是会想在心里呐喊,要么涨工资,要么涨工资,要么涨工资,为什么??因为不让我们加班,这是不可能的!!!

  想要颠覆自己的工作模式吗?想要减少自己的加班时间吗?加入我们,和我们一起探寻属于我们程序员的自由模式吧!

  一款针对程序员的原生APP,以共享知识技能为目的,以悬赏方式在线互动交互平台。

  我们拥有高达近20人顶尖的技术团队,以及优秀的产品及运营团队。团队领军人物均在行业内有10年以上的丰富经验。

  现在我们正在招募原始的参与英雄,您将同我们一起改变程序员的工作方式,改变程序员的世界!同时也会有丰厚的报酬。作为我们的原始的参与者,您将同我们一起体验这款程序员神器,您可以提出专业的建议,我们会虚心采纳。每一个人都会是英雄,而您就会是我们需要的英雄!同时您也可以邀请您的朋友一起参与这场英雄的招募互动。

  我们不会耽误你太多时间,我们只需要您的专业看法,只要您从一个月内抽出1个小时,以后您每天都可以节省两个小时,一切都是为了我们自己!

  来?还是不来?

  接头人暗号:1955246408 (QQ)

时间: 2024-10-07 06:40:45

iOS开发之GCD使用总结的相关文章

iOS开发之GCD 多线程 1.串行队列 2.并行队列 3.分组 4.信号量 详细讲解

GCD多线程下,实现线程同步的方式有如下几种: 1.串行队列 2.并行队列 3.分组 4.信号量 实例: 去网上获取一张图片并展示在视图上. 实现这个需求,可以拆分成两个任务,一个是去网上获取图片,一个是展示在视图上. 这两个任务是有关联的,所以需要同步处理. 下面看这几种方式如何实现. 一. 1.串行队列 1.1[GCD相关:] (1)GCD下的dispatch_queue队列都是FIFO队列,都会按照提交到队列的顺序执行. 只是根据队列的性质,分为<1>串行队列:用户队列.主线程队列 &l

iOS多线程开发之GCD(下篇)

上篇和中篇讲解了什么是GCD,如何使用GCD,这篇文章将讲解使用GCD中将遇到的死锁问题.有兴趣的朋友可以回顾<iOS多线程开发之GCD(上篇)>和<iOS多线程开发之GCD(中篇)>. 言归正传,我们首先来回顾下死锁,所谓死锁: 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源(如数据源,内存等,变量不是资源)而造成的一种互相等待的现象,若无外部处理作用,它们都将无限等待下去. 死锁形成的原因: 系统资源不足 进程(线程)推进的顺序不恰当: 资源分配不当 死锁形成的条件:

李洪强IOS开发之-iOS经典面试题

李洪强IOS开发之-iOS经典面试题 写这篇文章的目的是因为前两天同学想应聘iOS开发,从网上找了iOS面试题和答案让我帮忙看看.我扫了一眼,倒吸了一口冷气,仔细一看,气的发抖.整篇题目30多个没有一个答案是对的,总结这篇面试题的作者对iOS机制根本就是一知半解就敢发到网上,不管有心还是无心都是坑害新人.所以在这里总结一下这几年面试别人和被别人面试遇到的一些我认为比较好的基础题目分享给大家,进阶题目在后续补充.我的理解如果有错漏请一定指出,非常感谢! 从12年开始做面试官到现在已经三个年头了,这

iOS开发之 Method Swizzling 深入浅出

<p align="center"><img src ="https://raw.githubusercontent.com/DotzuX/Notes/master/logo.jpeg"/></p> iOS开发之 Method Swizzling 深入浅出 只要善用Google,网上有很多关于Method Swizzling的Demo,在这里我就不打算贴代码了,主要介绍下概念,原理,注意事项等等. 开发需求 如果产品经理突然说:&

IOS开发之copy的问题

copy的目的就是修改副本,修改原始对象和副本时不会产生干扰. 定义一个不可变属性A,再定义一个可变属性B.用B做添加删除等操作后再将B赋值给A时,有些人习惯用A = B:其实这样是不安全的. 假设有下面的一段代码: ? 1 2 3 4 5 6 7 8 9 10   int main() {    NSMutableString *strM = [NSMutableString [email protected]"123"];    NSString *str = strM;    N

iOS开发之WKWebView简单使用和常用使用场景

iOS开发之 WKWebVeiw使用 想用UIWebVeiw做的,但是突然想起来在iOS8中出了一个新的WKWebView,算是UIWebVeiw的升级版.本着对新事物的好奇,就上网查了一下,但是找了好多个都没说的多了详细,于是就问谷歌,找文档,看看使用方法,试用了一下,果然不错,记录下来,大家分享! WKWebView的特点: 性能高,稳定性好,占用的内存比较小, 支持JS交互 支持HTML5 新特性 可以添加进度条(然并卵,不好用,还是习惯第三方的). 支持内建手势, 据说高达60fps的刷

iOS开发之Auto Layout入门

随着iPhone6与iOS8的临近,适配的问题讲更加复杂,最近学习了一下Auto Layout的使用,与大家分享.  什么是Auto Layout? Auto Layout是iOS6发布后引入的一个全新的布局特性,其目的是弥补以往Autoresizing在布局方面的不足之处,以及未来面对更多尺寸适配时界面布局可以更好的适应. 为什么要用Auto Layout? Autolayout能解决不同屏幕(iPhone4,iPhone5,iPad...)之间的适配问题. 在iPhone4时代开发者只需要适

iOS开发之CocoaPods的使用

透明色:00ff00ff //设置柱状图的颜色                ColorSet cs = new ColorSet();                cs.Id = "colorset1"; #region 设置柱状图的颜色 待开发                    string strColor = oYAXIS.Color;                    switch (strColor)                    {           

iOS开发之UILabel

UILabel是iOS开发中常用的一个组件,主要用来显示内容. UILabel的主要使用如下: ? 1 2 3 4 5 6 7 8 9 10 /*尺寸*/ CGRect labelRect = CGRectMake(100, 100, 80, 40); /*初始化*/ UILabel *titleLabel = [[UILabel alloc] initWithFrame:labelRect]; /*一些属性的设置*/ titleLabel.font = [UIFont systemFontOf