GCD学习笔记

本文来源是基于http://www.raywenderlich.com/60749/grand-central-dispatch-in-depth-part-1 。这篇文章是基于该博客写的一个读书笔记,是对原文章的知识点进行重新梳理加工,加入我的个人理解,以及在工作中使用GCD的一些心得。本人能力有限,若是发现不足请在评论当中指出,我会在文章中更新,不胜感激!

GCD 特点?

1、GCD 通过将耗时任务放到后台线程执行来提交程序的响应性

2、相对于直接使用线程和锁的并发模型,GCD 更易于使用,且能够避免一些 bug

3、代码执行效率高

4、所有相关的方法都是以dispatch命名开头

并发的相关概念

1、串行和并行

串行是同一个时间内有且仅有一个任务在执行(场景类似单人过一线天,人可以比作任务),并行是同一个时间内可以有多个任务在执行(场景类似多人百米赛跑,人可以比作任务)。在GCD中,任务可以理解成一个Block。

2、同步和异步

同步是提交任务之后会等待提交的任务执行完成之后才做返回操作(场景类似监考,监考老师从考试开始时就一直呆在考室里面,直到考试结束后才走出考室)。异步就是提交任务之后,不管任务是否开始执行,立即做返回操作(场景类似布置作业,老师从上课开始来布置一次作业,然后让各位同学完成作业,布置完作业之后,老师马上就离开了)。

3、临界值

临界值可以理解成一块不能并发执行的代码,这块代码包含了一些共享的资源,不能允许多个线程同时访问(在几个口渴的人面前,只有一个水壶,这几个人只能一个一个直接用水壶喝水,水壶就是临界值)。

4、竞争条件

简而言之,多个线程在读写共享资源时,执行结果取决于线程的执行时间和执行顺序(在几个口渴的人面前,只有一个水壶,这几个人只能一个一个直接用水壶喝水,这几个人当中一个人能喝多少水取决于这个人是什么时候开始喝的水以及喝了多长时间)。

5、死锁

死锁是线程中比较悲剧的一个问题,简单来说就是,线程A等着线程B执行完成,线程B反过来等着线程A执行完成,好了,这样线程AB就一直耗着了,这就是死锁(用一个场景来描述这个死锁,男生心里暗恋一个女生,碰巧该女生也暗恋这个男生,2个人都在等着对方捅破最后的窗户纸,最后2人就没有浪漫的结果了,哈哈)。

6、线程安全

线程安全是一个比较常见的概念,线程安全的代码能够在多线程和并发访问下不会出现数据错误等问题,比如NSDictionary就是线程安全,它可以在同一时间内被多个线程访问而不会有任何问题,对比之下NSMutableDictionary就是非线程安全的,在同一个时间内只能有且只有一个线程操作它。(GCD的队列就是线程安全的)

7、上下文切换

上下文切换就是在一个进程内执行多线程任务时候,线程之间切换时候保存和恢复线程的执行状态,比如从主线程切换到其它线程,这个时候系统会保存主线程的执行状态(包括不限于变量),从其它线程切换到主线程时候系统会恢复之前保存的主线程执行状态。

讲完了并发的相关概念,接下来就开始讲和GCD直接相关的技术了(在本文中任务与Block是等同的)

队列

GCD提供了队列来处理任务(Block),队列使用FIFO(先进先出)的方法来管理你提交给GCD任务,也就是说在GCD的同一个队列里面,先提交的任务比慢提交的任务较先执行,也就是先来先执行。

1、串行队列(Serial Queues)

顾名思义,串行队列就是一个时间内只执行一个任务,只有前一个任务执行完成之后,才会开始执行下一个任务,任务的被执行顺序和任务被提交到GCD的顺序是一致的。示意图如下(来自网络)


行队列

2、有串行队列就会有并发队列(Concuttent Queues)

并发队列允许同一个时间内可以有多个任务在执行,不过和串行队列一致的一点就是任务的被执行顺序和任务被提交到GCD的顺序是一致的。对于GCD来说,任务先来先执行,不管你是什么类型的队列。示意图如下(来自网络)


并发队列

既然知道了并发队列和串行队列,那你也应该会猜到苹果肯定也会给几个默认的队列让我们这些开发者使用的,接下来就来说说队列的类型。系统提供了一个特殊的串行队列给我们使用,这个队列就是主线程队列(main queue),它除了具备串行队列的一个特点之外(一个时间内只能执行一个任务,只有前一个任务执行完成之后,才会开始执行下一个任务)还有一个大杀器,就是所有的任务保证都会在主线程中执行,这意味着什么呢?意味着我们可以使用这个队列来更新UI。(不管iOS还是Android系统,所有的UI操作都是要在主线程中来完成的)。

说完串行队列说说系统提供的并发队列吧!名字叫做全局分发队列(Global Dispatch Queue),它有四个不同优先级别的并发队列,优先级别分别是Background,low,default,high。使用这个全局分发队列的时候并不是设置优先级别越高越好,因为苹果的Api也是使用这些队列,所以在这些个队列中已经有存在一些任务,通常的使用方法是设置默认的优先级别default。

关键点 dispatch_async,dispath_sync,串行队列,并行队列

讲了这么多,还没有上代码,好烦。我就图方便,直接用他人的现有案例来说吧,http://cdn4.raywenderlich.com/wp-content/uploads/2014/01/GooglyPuff_Start_1.zip  (初始案例)

这是一个图片案例,从网络上加载图片,加载完成后点击进入查看大图,在大图里面识别出人物的眼睛,并给眼睛贴上图片。

项目截图
案例结构说明


在开发中,为了有较好的用户体验,通常会把一些耗时的操作(文件操作,数据库操作,网络操作,图片编解码)放到后台线程去操作,以避免耗时操作阻塞主线程。在被注释掉的代码中直接在UI主线程进行图片对象的处理(对人物的眼睛进行识别然后贴图),这个是一个耗时操作,所以对它进行改进。

改进之后,

1、将这个耗时操作放到后台线程去操作,使用 dispath_async 异步提交任务到全局并发队列(优先级为高),然后不管任务现在是否开始执行,立即执行下一个代码NSLog打印内容。

2、在第二个dispath_async中,此时上下文环境为后台线程,那么要更新UI的话必须要在主线程内更新,所以使用dispath_async异步提交更新UI的任务到主线程(dispatch_get_main_queue()获取串行主线程队列),dispath_async为异步执行,不会等待任务执行完返回,而是立即返回。

3、使用UIImage对象更新UI。

改进之后

从上面的代码可以看到,dispath_async添加Block到一个队列中然后马上返回,这个Block在之后的某个时间点会被GCD执行。那么什么时候使用dispath_async呢?网络操作和CPU密集型的任务都可以放到后台去执行,这样做的好处就是不会阻塞当前主线程。

dispath_async和各个类型的队列配合:

1、自定义串行队列

如果你想要在后台线程中连续执行任务,并且跟踪任务的执行,那么dispath_async+自定义串行队列是你的一个不错的选择。

自定义串行队列

2、主线程串行队列

在后台并发队列执行完任务之后,通常会跳到主线程更新UI。

主线程串行队列

3、后台线程执行耗时的非UI任务

dispath_sync和各个类型的队列配合:

1、自定义串行队列,在主线程内同步执行dispath_sync,将任务提交到自定义串行队列执行。

2、主线程串行队列,在主线程同步提交任务到主线程串行队列会造成死锁,死锁,死锁。

代码中的dispath_sync提交任务到主线程串行队列之后,没有马上返回而是在等待任务执行完成。而在主线程串行队列,已经有dispath_sync这个方法在执行了,主线程队列等dispath_sync这个方法执行完成后,才执行dispath_sync的提交任务。dispath_sync提交的任务等待主线程,主线程等待dispath_sync方法,你等我,我等你,大家一起Over!

3、并发队列,dispath_sync和并发队列一起使用是无法达到任务在后台运行的效果的,因为并发队列的任务都是在主线程中,并且无法体现出并发队列的并发特性

关键点 dispatch_after

有些时候我们会有类似需求,比如在用户登录成功几秒后开始从服务器同步数据到本地。可以使用dispatch_after来完成类似延迟任务。在当前案例中,我们使用dispatch_after来延迟做出提示。

先创建一个dispatch_time_t 说明从什么时候开始算起延迟多少时间,延迟时间的单位是毫秒。在延迟时间到达之后dispatch_after提交任务给GCD。GCD根据情况安排任务的执行。也就是说这里的延迟是延迟任务的提交时间,而不是说延迟多少时间后任务开始执行。

dispatch_after和各个类型的队列

1、自定义串行队列(Custom Serial Queue),不建议在这个队列中使用。

2、主线程队列(Main Queue)使用dispatch_after的好地方。

3、并发队列(Concurrent Queue,不建议在这个队列中使用


关键点 线程安全的单例对象

在使用单例模式的时候,最重要的就是当有多个线程同时访问单例对象的时候,单例对象需要时刻保持有且只有一个对象,数据读写线程安全。先看看案例

这个sharedManager方法如果有多个线程同时访问的话,那么就会出现创建多个sharedPhotoManager对象的问题。启用2个并发任务来访问sharedManager方法,

sharedManager方法做一些休眠操作和打印内存地址操作,这样的目的就是让2个线程同时可以访问sharedManager方法

Xcode输入的sharedPhotoManager对象内存地址表明,创建了3个sharedPhotoManager对象,那么这个作为单例肯定是很坑爹的。

关键点 dispatch_once

dispatch_once()方法会以线程安全的方式有且仅有一次执行Block里面的方法,这样当多个线程同时访问sharedManager方法的时候就不用担心会出现多个对象被创建的情况了,哈哈,简单吧!

说完了初始化的问题,接下来就是getter和setter方法的问题了。dispatch barriers是GCD提供的优雅的读写锁解决方案。

看图说话,在dispatch barriers 执行前任务是并发执行的,但是到了dispatch barriers后,并发执行全部都变成了串行执行,当dispatch barriers执行完毕后,任务恢复成以前的状态也就是并发执行。也就是说在当前的队列中,当任务执行到dispatch barriers的Block的时候,dispatch barriers可以保证此时有且仅有这个Block在执行,当这个Block执行成功后,队列恢复正常。(这个类似多人过独木桥,有多个人并排走到一座独木桥前,这个独木桥每次只能一个人通过,且桥上一次只能站一个人,那么多个人就是单个单个的过独立桥,过桥之后恢复过桥之前的状态)。

dispatch barriers和各个类型的队列

1、自定义串行队列(Custom Serial Queue), 串行队列本来就是一次只能执行一个任务和dispatch barriers搭配没有什么用处。

2、并发队列(Global Concurrent Queue),系统的API也有使用这些队列,为了不影响系统API执行,最好不要在这些队列使用。

3、自定义并发队列(Custom Concurrent Queue),墙裂推荐,需要保证setter和初始化方法线程安全的都是可以使用这个搭配。


解决getter和setter方法的线程安全问题

dispatch_barrier_async在setter方法保证往_photosArray加入photo是线程安全的,至于getter方法使用NSArray保证了array不会被误修改.

最后的工程 http://cdn2.raywenderlich.com/wp-content/uploads/2014/01/GooglyPuff_End_1.zip

如果你觉得我的这篇文章对你有一丁点儿作用的话,那么希望你能在下方给个赞哈,让我知道这文章已经起了它应该的作用,谢谢!

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

推荐阅读更多精彩内容

  • 从哪说起呢? 单纯讲多线程编程真的不知道从哪下嘴。。 不如我直接引用一个最简单的问题,以这个作为切入点好了 在ma...
    Mr_Baymax阅读 2,752评论 1 17
  • 1. iOS中多线程的四种方案 iOS中实现多线程目前有4种方案,最常用的是GCD和NSOperation两种,而...
    TedX阅读 1,565评论 3 2
  • GCD 深入理解:第一部分 什么是 GCD GCD 是 libdispatch 的市场名称,而 libdispat...
    willphonez阅读 623评论 0 2
  • 虽然 GCD 已经出现过一段时间了,但不是每个人都明了其主要内容。这是可以理解的;并发一直很棘手,而 GCD 是基...
    随风飘荡的小逗逼阅读 1,330评论 0 2
  • 1.他怎么结婚了? 曾经听人说,如果有一个人总是出现在你的梦中,那不是说明你想那个人,而是说明那个人想见你。 于是...
    麦筱柒阅读 3,199评论 13 55