iOS GCD (三)

dispatch_suspend/dispatch_resume

有个时候会有这样的需求场景,当追加大量的处理到Dispatch Queue中,在追加的处理的过程中,希望已经追加的处理不执行。例如,将要追加的block已经截获到了结果值,而已经追加的处理如果继续执行,则会改变这个值。
在这种情况下, 只需要挂起Dispatch Queue就可以,当可以执行时再恢复。
dispatch_suspend函数挂起指定的Dispatch Queue

dispatch_suspend(globalQueue);

dispatch_resume函数恢复指定的Dispatch Queue

dispatch_resume(globalQueue);

这些函数对已经执行的处理没有影响,挂起后,追加到Dispatch Queue中的但尚未执行的处理在此之后停止执行,恢复后继续执行。

dispatch_once

dispatch_once函数可以保证在应用程序的执行中,只执行一次指定的处理。即使在多线程环境下,也可以保证百分百安全。多用于单利模式

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"执行一次性的任务")
    });

在xcode中是个默认的代码块,输入“dispatch_once”选择“dispatch_once snippet...”即可。

dispatch_semaphore

先看一段代码

    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSMutableArray *marr = [[NSMutableArray alloc] init];
    for (int i = 0; i < 10000; i++) {
        dispatch_async(globalQueue, ^{
            [marr addObject:@(i)];
            NSLog(@"%d",i);
        });
    }

由于该源码使用Global Dispatch Queue更新NSMutableArray类的对象,很容易因为内存错误导致应用程序异常结束。这就需要用到dispatch_semaphore。

semaphore 是信号、旗语的意思,我们就在这里理解为信号通道吧。它的作用是把可同时执行多个线程的并行队列,压缩成只可以同时执行小于等于指定数量线程的队列。
dispatch_semaphore有三个函数

  • dispatch_semaphore_create 创建信号通道
dispatch_semaphore_create(<#long value#>)

参数为允许信号个数,必须大于0,否则返回NULL,大于1则返回dispatch_semaphore_t类型对象,即一个允许的信号数为n的信号通道

  • dispatch_semaphore_wait 等待当前的队列中线程数量和信号通道都满足条件
dispatch_semaphore_wait(<#dispatch_semaphore_t  _Nonnull dsema#>, <#dispatch_time_t timeout#>)

第一个参数为dispatch_semaphore_t类型变量,即信号通道;
第二个参数为dispatch_time_t类型值,
当取值为DISPATCH_TIME_FOREVER时,程序就会一直等待,直到队列中的线程数大于等于1,且信号通道允许的信号数大于1,此时函数返回0,且程序继续往下执行。
如果此参数指定一个时间段,比喻说5s,如果在5s内队列中的线程数大于等于1,且信号通道允许的信号数大于1,则此函数返回0,并且程序继续往下执行,否则经过5s线程数依然小于等于0,或者信号通道允许的信号数小于1,则函数返回非0,程序继续往下执行。
另外,当dispatch_semaphore_wait函数返回0的同时,会将信号通道允许信号数减一。

  • dispatch_semaphore_signal 使指定信号通道的允许信号数加一
dispatch_semaphore_signal(<#dispatch_semaphore_t  _Nonnull dsema#>)

参数为指定的信号通道。
函数的功能是使信号通道允许信号数+1,然后唤醒等待的线程,唤醒成功返回非0,否则为0。

完整使用

    // 创建一个信号通道,允许的信号数是1
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSMutableArray *marr = [[NSMutableArray alloc] init];
    for (int i = 0; i < 10000; i++) {
        dispatch_async(globalQueue, ^{
            /* 等待队列中可执行的线程数大于等于1,如果在10s之内队列中可执行线程数大于等于1,继续判断信号通道允许的信号数是否大于1,大于1则往下执行,
            同时函数dispatch_semaphore_wait返回0,且将信号通道允许的信号数-1(表示此时信号通道被占用),
            这样信号通道的允许信号数为0(如果一直为0,队列中的任务在下一循环中将一直等待指定时间消耗完,程序才会往下执行,函数返回非0)
             如果在10s之后等待失败(线程数小于1或者信号通道允许信号数为0),函数dispatch_semaphore_wait返回非0,继续往下执行
             */
            long result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)));
            if (result == 0) {
                [marr addObject:@(i)];
            }
            NSLog(@"%d",i);
            // 将信号通道的允许信号数加一,并唤醒其他等待的线程,唤醒成功返回非0 ,唤醒失败返回0,这样信号通道中允许的信号数为1,下一个循环中,只要队列中的线程大于1就可以正常执行了。
            dispatch_semaphore_signal(semaphore);
            
        });
    }

打印结果太多,截取部分如下

2018-10-09 15:16:19.722482+0800 GCDTest[9209:2227242] 0
2018-10-09 15:16:19.722754+0800 GCDTest[9209:2227240] 1
2018-10-09 15:16:19.722923+0800 GCDTest[9209:2227239] 2
2018-10-09 15:16:19.723054+0800 GCDTest[9209:2227429] 3
2018-10-09 15:16:19.723202+0800 GCDTest[9209:2227430] 4
2018-10-09 15:16:19.723388+0800 GCDTest[9209:2227431] 5
2018-10-09 15:16:19.723513+0800 GCDTest[9209:2227433] 7
2018-10-09 15:16:19.723650+0800 GCDTest[9209:2227432] 6
2018-10-09 15:16:19.723764+0800 GCDTest[9209:2227242] 9
2018-10-09 15:16:19.724041+0800 GCDTest[9209:2227434] 8
2018-10-09 15:16:19.724964+0800 GCDTest[9209:2227435] 10
2018-10-09 15:16:19.725338+0800 GCDTest[9209:2227436] 11
2018-10-09 15:16:19.725754+0800 GCDTest[9209:2227437] 12

注释比较清楚可以仔细看看注释。

根据上面的执行结果和代码分析,我们可以知道以下结果

1.如果注释掉代码dispatch_semaphore_signal(semaphore),那数组marr 中只会有一个对象,因为dispatch_semphore_wait函数只在首次调用时等待成功过;
2.dispatch_semphore_wait 会阻塞队列中的线程。但是不会阻塞主线程,也不会影响for循环,for循环很快就执行完成了,也就是说,队列中在for循环执行完成时,就已经被追加了10000个任务了。只是这些任务受信号管道限制而不能随意执行;
3.任务执行是无序的。

这些都是可以验证的,这里略去不表。

还有个问题,想不明白,就是注释掉dispatch_semaphore_signal(semaphore)后,执行完所有的任务后程序会崩溃。不知道是什么原因,可能跟dispatch_semphore_wait和dispatch_semaphore_signal要成对出现吧。如果有哪位大神知道,还请多多指点


根据这个图片可以看出for循环不会被阻塞,阻塞的是队列中的任务。

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

推荐阅读更多精彩内容