GCD 系列知识总结(NSOperation比较)

1. 创建队列

//自己创建串行队列

dispatch_queue_t dySerial = dispatch_queue_create("串行队列", DISPATCH_QUEUE_SERIAL);

//自己创建并行队列

dispatch_queue_t dyConcurrent = dispatch_queue_create("并行队列", DISPATCH_QUEUE_CONCURRENT);

//系统默认主队列,就是主线程

dispatch_queue_t osMainSerial = dispatch_get_main_queue();

//系统默认的全局并行队列

dispatch_queue_t osConcurrent = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

2. 执行列队

//异步执行      dispatch_async("传入队列", 任务block);

//同步执行       dispatch_sync("传入队列", 任务block);

3. 不同执行 + 不同队列效果

3.1 同步执行 + 并行队列 不会创建多线程 顺序执行

3.2 异步执行 + 并发队列 会创建多个线程

3.3 同步执行 + 串行队列 不会创建新线程

3.4 异步执行 + 串行队列 只创建了一个线程(区别于当前线程的新线程)

3.5 同步/异步 + 主队列dispatch_get_main_queue()执行效果

3.5.1  特别注意: 同步执行 + 对于特殊的串行队列 dispatch_get_main_queue() 会出现死锁

首先执行任务1,然后遇到dispatch_sync 同步线程,当前线程进入等待,等待同步线程中的任务2执行完再执行任务3,这个任务2是加入到mainQueue主队列中。

dispatch_sync(osMainSerial,^(void)()) 是同步一个任务到主队列中,而当前去同步的正是主队列。因为队列的执行是FIFO(先进先出),所以两个都在等待对方完成,就会造成死锁。

3.5.2 如果这个操作放在非主队列中执行就不会有问题

3.6 异步执行 + 对于特殊的串行队列 dispatch_get_main_queue() 异步执行的时候 不会创建新线程,只会使用当前线程

4. 关于死锁

4.1 在上3.5.1上介绍 同步执行(dispatch_sync) + 主队列 = 死锁。那么如果同步执行的 + 非主队列 是否就一定不会出现死锁了?看下面的代码:

会发现还是会出现死锁的情况,综上所述不难发现这种死锁产生的原因:

在串行队列A(主队列或自己创建的串行队列)的线程中同步执行一个任务,而且这个任务还是放在串行队列A中。这样就会出现死锁。

4.2 还有一种死锁,就是当前的串行队列被阻塞,然后同步任务到这个队列就会出现

执行顺序:

main队列中死循环不结束,任务3不执行,任务3又是global全局队列同步过去的,global中同步的任务3不执行完、任务4是不会执行的。

了解死锁的发生时机后就很容易理解,所谓队列(串行并行)就是一个顺序执行的甬道。所谓同步异步就是决定执行这个队列的时候是否开辟新的线程去执行。

关于GCD之间的通信,就是一个概念记住就好。不同的队列直接相互通信,只需要在执行任务的时候同步或异步执行任务的时候调用别的队列传值

5.  GCD常用方法

5.1 组操作dispatch_group

5.1.1  dispatch_group_notify、dispatch_group_wait 基本用法

异步执行多个耗时任务,然后多个任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组

dispatch_group_notify和dispatch_group_wait意思就是notify有block你可自己干某事,一个没block 干某事只能接着wait后面写顺序执行。

5.1.2  dispatch_group_enter、dispatch_group_leave 基本用法

先看一个例子

发现doSomeThing1还没执行完就进入dispatch_group_notify打印刷新页面,这是为什么了?看代码会发现doSomeThing1是放在了一个异步线程中执行的。但是dispatch_group_notify只会监听dispatch_group_async中执行的同步任务。如果dispatch_group_async执行的是个异步任务,那notify 监听结果可能不是你想要的。

那我们能不能实现dispatch_group_async执行多异步任务,同时还能监听到任务全部完成了?做到这些就需要dispatch_group_enter和dispatch_group_leave了,看下面例子:

dispatch_group_enter 用来标记这个任务的开始,dispatch_group_leave用来标记任务完成。这样成对标记可以实现标记dispatch_group_async任务的真正执行完毕,dispatch_group_notify也就能监听到了。

记住:dispatch_group_enter 和 dispatch_group_leave 是成对出现的,不然dispatch_group 队列会出错。

5.1.3 用dispatch_group 处理多网络请求都返回在刷新页面

根据5.1.2 我们可以在实际开发中运用dispatch_group 来处理多个网络请求都完成的通知。因为我们的网络请求都是异步的,正好可以用dispatch_group_enter 和 dispatch_group_leave实现标记完成

5.2 只执行一次 dispatch_once

创建单例或者有整个程序运行过程中只执行一次的代码时使用。保证某段代码在程序运行过程中只被执行1次,并且即使在多线程的环境下,dispatch_once也可以保证线程安全。

5.3 延时执行方法:dispatch_after

在指定时间(例如3秒)之后执行某个任务。dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的

5.4 栅栏方法 dispatch_barrier_async

我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于栅栏一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏。

发现:任务1和任务2 全部执行完毕后 再执行追加任务,然后再执行任务3和任务4,通过这个例子我们能明白dispatch_barrier_async主要是分割异步任务的作用。

5.5  dispatch_apply 快速遍历

和一般我们用for循环遍历不同的是,如果dispatch_apply调用的时候传入queue的是并发队列,那么它的遍历就是异步执行的,会在多个线程中进行,而我们直接for循环都是在当前线程中进行的。如下

发现打印的线程号不是按顺序的,但是如果传入的是个串行队列(非主队列)它就和普通的for循环没什么不同了。如下

发现输出是按照顺序的,和for循环没什么不同,都是在主线程中进行的。

注意:如果执行dispatch_apply的线程是串行队列A的线程,同时执行dispatch_apply时传入queue的也是串行队列A,会发生死锁。比如直接在主队列中执行dispatch_apply,同时传入queue也是主队列,如:

当然这样做也没什么意义,一般dispatch_apply都是为了高效的异步的遍历数组。所以一般dispatch_apply传入的queue都是一个并发队列,同时还把dispatch_apply整体放入一个异步执行的并发队列中,如:

5.6 信号量 dispatch_semaphore

使用dispatch_semaphore 可以更好的控制并发多线程的任务处理。

5.6.1 控制并发线程数量

现在有一组图片地址,需要开辟一个并发队列来下载这些图片,需要等这些图片都下载完了才去做下面的事情

直接运行会发现会一下开辟imageArray.count个线程来处理任务。假如这里imageArray.count是100多,很显然,这样的暴力处理是不切合实际的。使用Semaphore我们可以控制同时并发的线程数量。效果如下:

发现我们先开辟5个线程去下载,5个都下载完成了后再开辟5个继续下载,这样就可以减轻系统并发的数量。

看结果你是不是感觉这有点像dispatch_barrier_async都是把任务拆成几部分一块一块执行,但是他们是有本质不同的:dispatch_barrier_async控制的是任务,它操作的对象是任务分块,不会处理线程的多少,而Semaphore控制同时并发的线程数量,再由一定的线程数量去执行任务。这好比同样是盖楼要10个人,barrier类似于我把楼分成2部分,我们10个人先盖第一部分,第一部分完了再盖第二部分。而Semaphore是我们同时只让5个人盖楼,剩下的休息,谁干完了谁休息,之前休息的接着上来干。

那如果dispatch_semaphore_create(5)改成dispatch_semaphore_create(1)会发现虽然调用的是并发队列,但是限制信号量总数为1 后,并发队列就变成了串行队列

5.6.2 对多线程并发执行任务共享资源进行  加锁处理

案例:总共有20张火车票,有两个售卖火车票的窗口,一个是北京火车票售卖窗口,另一个是上海火车票售卖窗口。两个窗口同时售卖火车票,卖完为止。

这里的问题关键是,两个窗口同时卖票,在卖出一张票后总票数都会减1,如何确保票不多卖或卖叉?这就相当于计算机中的多条线程同时写一个资源,可能会出现混乱的问题

观察发现余票在 11、6的时候出现混乱,多卖了票。这就是多线程访问公共资源出现不同步的问题。我们使用dispatch_semaphore 来加锁处理一下,看看结果怎么样?

发现总剩余票数没有发生错位。其实dispatch_semaphore这里就是控制了多线程处理任务 并发时机,也是5.1.1的一种特殊形式。

5.6.3 页面有多个网络请求都完成后才处理任务

上面说的使用dispatch_group_enter和dispatch_group_leave可以实现多网络请求完成监听处理。我们使用dispatch_semaphore也可以完成这种操作

补充: 那么如何让这些网络请求有个先后顺序了,就是说请求1返回回来的参数我要用到请求2上,这样的需求也很常见,用到的知识就不是GCD的了,而是NSOperation了

其中的[self request]里面代码还是使用信号量来控制网络请求真正结束

对于NSOperation实现多线程的方式有两种:

1.将要执行的任务封装到一个 NSOperation 对象中

2.将要执行的任务添加到一个 NSOperationQueue 对象中

NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation 。创建一个 Operation 后,需要调用 start 方法来启动任务,它会默认在当前队列同步执行。你也可以调用 cancel 方法在中途取消一个任务。

总结:NSOperation比GCD的好处

1、NSOperation能通过KVO键值观察者来实时监控operation的状态(是否执行,是否取消), 而GCD无法通过KVO来实时监控。通过completBlock来回调已经执行完毕。

2、NSOperation能通过设置依赖,使任务之间有先后顺序。GCD则可以通过栅栏函数来实现。

3、NSOperation能设置并行队列中任务的优先级(优先级的作用是让优先级高的线程需要cpu时能够更大几率优先得到cpu),GCD是设置不同任务队列(queue)的优先级,要实现block的优先级,需要很多代码。

4、NSOperation是将任务放入队列中来执行的。GCD是将任务放入队列中,通过同步异步函数来执行(可以多个同步异步函数)。

5、总体来说GCD更简洁

补充:

1、怎么取消正在执行的gcd任务:

可以仿照operation的工作原理,设置一个BOOL变量,当需要停止时设置成YES,执行任务的时候去判断这个状态

2、多线程使用带来的问题:资源竞争、优先倒置、死锁


以上是根据别人的博客和自己代码实践所得,如有冒犯和不足欢迎大家批评。

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

推荐阅读更多精彩内容

  • 文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法。通过本文,您将了解到: 1. GCD 简介 2. G...
    晓_我想去环游世界阅读 1,138评论 2 8
  • 本文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法。这大概是史上最详细、清晰的关于 GCD 的详细讲...
    花花世界的孤独行者阅读 498评论 0 1
  • 引言: 越是细节越能体现一个人的严谨,越是微小越是能看到事物的光芒 1. 队列 官方解释:DispathQueue...
    Chendy_Linda阅读 2,488评论 2 10
  • 1 他和她相识10年,在这个城市里相伴10年。 十年里足迹踏过这个城市的角角落落,看尽了这个城市一年四季,尝遍数不...
    白玛拉姆阅读 2,639评论 71 72
  • 下载、解压 mysql官网下载(对应版本的话就自己选择),本帅这里选择的社区版的。下载截图 下载完这是一个压缩包,...
    潘帅次元阅读 587评论 0 0