GCD实战攻略

GCD是苹果为多核并行运算提出的方案,可以更高效的利用CPU。但是更重要的是它使多任务处理更加高效。因为它会自动合理的运用多核CPU。并且自从GCD的内存管理也加入ARC之后,它就能自动管理线程的生命周期。我们只需要把需要的操作(block)告诉它就可以了。

凡说GCD,肯定要说两个概念

任务和队列,任务可以同步执行和异步执行,存放任务的队列分为串行队列和并行队列。

一.任务 (block)

就是咱们要执行的操作,GCD中就是block,任务执行在哪个线程由执行方式来决定

1.同步执行(sync)

会阻塞当前任务所在的线程(同步线程),dispatch_sync(queue, ^(block))把block放到queue中在当前线程执行。

2.异步执行(async)

不会阻塞当前任务所在的线程(异步线程),dispatch_async(queue, ^(block)),如果是串行队列,则只开一个线程,如果是并行队列,则开多个线程。

所以说sync和async决定block在哪个线程中执行

二.队列 (queue)

存放任务的地方,负责调度任务(block)

1.串行队列(SERIAL)顾名思义就是按顺序执行队列中的任务,一个任务完成再执行下一个任务。

主队列:dispatch_get_main_queue 是一个特殊的串行队列,运行在主线程中,UI相关操作都要在该队列中执行。

自定义串行队列:dispatch_queue_create("标识", DISPATCH_QUEUE_SERIAL),最后一个参数可以为NULL,默认创建的是串行队列

2.并行队列(CONCURRENT)就是很多任务并发执行,其实GCD中的并行队列也是根据FIFO的原则取出任务,不同的是取出任务后GCD会新开一个线程来执行任务。

全局队列:dispatch_get_global_queue(优先级,0); 苹果公开的全局并行队列,一共有四个优先级

自定义并行队列:dispatch_queue_create("标识", DISPATCH_QUEUE_CONCURRENT)


三.队列和任务是怎么执行的

GCD的基本概念虽然不多,但是用起来还是需要理解的深刻一些,比如任务在不同的队列类别里用不同的方式执行会造成什么样的结果,接下来咱们就来一项一项说。

1.串行队列同步执行 

2.串行队列异步执行

串行队列同步/异步执行任务

执行结果:


串行队列执行结果

1-4为串行队列同步执行

5-6为串行队列异步执行

可以看出来:1-4在当前线程一个一个执行,5-6新开了一个线程一个一个执行(如果串行队列为主队列,则在主线程中执行)

3.并行队列同步执行 4.并行队列异步执行

并行队列同步/异步执行任务

执行结果:

并行队列执行结果

1-4为并行队列同步执行

5-6为并行队列异步执行

可以看出来:1-4在当前线程一个一个执行,5-6新开了多个线程并发执行

四.实战场景

理解了任务、队列的运行规则,下面就来看看实际项目中在何种场景下运用GCD

1.运用dispatch_async来避免一些耗时的任务阻塞主线程(卡死界面).

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        NSLog(@"耗时的操作读取数据库,网络操作等");

        dispatch_sync(dispatch_get_main_queue(), ^{

              NSLog(@"操作完成_刷新UI在:%@",i,[NSThread currentThread]);

        });

});

这里可能有人会问:回到主线程的操作,dispatch_sync和dispatch_async有什么区别?

大家可以想想这个问题,如果理解了前面说的队列和任务,你应该知道结果,文章后面还会提到。

2.运用dispatch_apply进行快速迭代.

如果是串行队列,dispatch_apply和for是一样的,

如果运用在并发队列,那么dispatch_apply就会并发的执行任务(block),大大的提高遍历速度。

dispatch_apply


dispatch_apply执行结果

> 两种方法各循环100000次,for耗时32.9秒,apply耗时19.4秒

> 效率提高了40%多。

有人说可以在for循环里面用dispatch_async开启子线程执行任务,的确可以,但是如果开启的子线程多了,很有可能线程爆炸造成死锁等情况。而GCD会管理并发,所以apply还能避免线程爆炸的问题,实在是居家旅行、杀人灭口,必备良药。

3.dispatch_group 调度组

开发中我们经常会遇到这样的需求:同时调用多个接口,所有接口返回后再刷新界面。如果不用调度组的话,应该怎么做?大部分人都会把这几个接口串行起来,等最后一个接口返回再执行刷新界面的方法,这样做的话如果其中一个接口返回失败,那么整个页面就无法刷新了。还有的同学每个接口回来都刷新一次界面,这样会造成页面闪烁,严重的话,并发调用同时回来结果操作同一数据源,还有可能造成崩溃。

这时无疑用GCD调度组是最好的解决方法。调度组会在组里所有的任务执行完毕后发送一个通知告诉我们,组内的任务全部执行完毕。

接收通知的方式有两种,

同步执行的dispatch_block_wait,会阻塞当前线程并等待之前的任务全部执行完或超时再执行wait中队列里的任务

异步执行的dispatch_group_notify,作用和wait一样,但是是异步执行的,所以不会阻塞当前线程

dispatch_group_enter和dispatch_group_leave可以手动管理group中的任务计数,enter为+1,leave为-1,当计数为0时,才会进入wait或notify中的任务

具体执行看代码:这里用after延迟提交任务(block)的做法来模拟调用接口时的情景。


调度组方法执行结果

以上代码中创建了一个调度组group,并且指定调度组中的任务在全局队列中运行。在执行结果中可以看到,全局队列中先执行了任务2和任务1(强制停止了1秒),因为wait会在当前线程等待任务1,2都完成之后才执行,所以wait执行完后再执行任务3,4,这里用了手动计数的方法控制任务计数,当3,4都执行完后,计数归0,最后计入notify的任务,当我们需要同时并发执行多个接口之后再执行某项操作时,调度组非常实用。

4.GCD中容易遇到的死锁问题

GCD的任务和队列都是在线程中运行的,所以频繁的操作线程如果不注意会很容易造成死锁问题,所谓死锁,就是指两个线程互相等待对方完成某项操作,导致线程卡死,当然归根结底是对任务和队列的运行方式理解的还不够彻底,下面列举一些容易造成死锁的现象,coding中要格外注意。

案例1:最简单的死锁现象

控制台只输出了1

控制台:

1.

我们来分析一下堆栈信息,根据FIFO的原则,任务1 - 同步线程-任务3-任务2

因为同步线程阻塞了主线程,所以任务2等待任务3执行,任务3又等待任务2,造成死锁,程序卡死。

这时如果我们再稍微复杂一些呢


如果大家理解了这个案例,相信自己就能回答出来了


案例2:串行队列中同步执行一个并行队列中的任务


同步运行全局队列中的任务


控制台:

1

2

3

这个比较好理解,在主线程中打印1,这时同步线程在全局队列里面执行2,不会像1一样把任务2直接加在主队列队尾,所以不存在2,3相互等待的情况,而是同步线程阻塞了主线程后等待任务2执行完毕后,顺序执行3.

案例3:异步线程执行后回到同步线程执行


是不是很眼熟,这就是4.实战场景中的第一个案例,我们当然不是要演示这个,继续看下图两张图


控制台:

1

8

2

3

4

5

6

7

控制台:

1

8

2

7

3

4

5

6


控制台:


1,4顺序不一定

分析:输出0后,就是异步线程,所以任务4不用等待,1,4执行顺序不一定。任务4完成后,接着是一个阻塞线程的同步任务5,但是加入到全局队列的异步线程不受影响,继续执行1后面的同步线程中的任务2,并且任务3需要等任务2完成后才能执行。但是这时主线程已经被一个同步线程的任务5,和6死锁,所以任务2也无法执行。

总结

GCD除了上面说的这些,只要理解了他的运行方式,可以灵活的组合出很多用法,当然这篇文章只是说了GCD的凤毛麟角,NSOperation和NSOperationQueue还没有提到,先理解了GCD,咱们下次说NSOperation和NsOperationQueue。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容