简单掌握GCD系列二

GCD中除了上篇文章提到的 dispatch_sync 和 dispatch_async 函数。还有一些虽不是很常用,但功能还是很强大的函数。跟着我一一来学习下吧。

1.dispatch_set_target_queue

dispatch_queue_create 函数生成的 Dispatch Queue 不管是 Serial Dispatch Queue 还是 Concurrent Didpatch Queue,执行优先级与默认优先级的 Global Didpatch Queue 相同。而变更生成的 Dispatch Queue 的执行优先级要使用 dispatch_set_target_queue 函数。当然iOS8之后,出了新的函数 dispatch_queue_attr_make_with_qos_class 也可以实现改变 Dispatch Queue 的执行优先级。两种方式如下。

iOS8之前,改变 Didpatch Queue 执行优先级代码如下

dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL);

dispatch_queue_t golbalQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

dispatch_set_target_queue(myQueue, golbalQueueBackground);

iOS8之后,改变 Didpatch Queue 执行优先级代码如下

dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_BACKGROUND, 0);

dispatch_queue_tmyQueue =dispatch_queue_create("myQueue", attr);

这两种方式都可以获得一个优先级为 backgroud 的 Serial Queue。
dispatch_queue_create 函数除了可以改变 Didpatch Queue 的执行优先级,还有一些隐含功能。比如在多个 Serial Dispatch Queue 中用 dispatch_set_target_queue 函数指定目标为某一个 Serial Dispatch Queue,那么原先本应该并行执行的多个 Serial Dispatch Queue,在目标 Serial Dispatch Queue 上只能串行执行。Concurrent Dispatch Queue 用 dispatch_set_target_queue 函数指定目标为某一个 Serial Dispatch Queue,Concurrent Dispatch Queue 中的多个任务也是变成串行执行。

    dispatch_queue_t serialQueue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t concurrentQueue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t serialQueue3 = dispatch_queue_create("queue3", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue4 = dispatch_queue_create("queue4", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue5 = dispatch_queue_create("queue5", DISPATCH_QUEUE_SERIAL);
    
    dispatch_queue_t golbalQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_set_target_queue(targetQueue, golbalQueueBackground);
    
    dispatch_set_target_queue(serialQueue1, targetQueue);
    dispatch_set_target_queue(concurrentQueue2, targetQueue);
    dispatch_set_target_queue(serialQueue3, targetQueue);
    dispatch_set_target_queue(serialQueue4, targetQueue);
    dispatch_set_target_queue(serialQueue5, targetQueue);
    
    dispatch_async(serialQueue1, ^{
        NSLog(@"---任务1---%@", [NSThread currentThread]);
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"---任务21---%@", [NSThread currentThread]);
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"---任务22---%@", [NSThread currentThread]);
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"---任务23---%@", [NSThread currentThread]);
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"---任务24---%@", [NSThread currentThread]);
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"---任务25---%@", [NSThread currentThread]);
    });
    dispatch_async(serialQueue3, ^{
        NSLog(@"---任务3---%@", [NSThread currentThread]);
    });
    dispatch_async(serialQueue4, ^{
        NSLog(@"---任务4---%@", [NSThread currentThread]);
    });
    dispatch_async(serialQueue5, ^{
        NSLog(@"---任务5---%@", [NSThread currentThread]);
    });

上面代码执行结果如下

2018-12-08 12:48:56.743282+0800 test[56851:22943615] ---任务1---<NSThread: 0x1d447b4c0>{number = 3, name = (null)}
2018-12-08 12:48:56.743378+0800 test[56851:22943615] ---任务21---<NSThread: 0x1d447b4c0>{number = 3, name = (null)}
2018-12-08 12:48:56.743409+0800 test[56851:22943615] ---任务22---<NSThread: 0x1d447b4c0>{number = 3, name = (null)}
2018-12-08 12:48:56.743429+0800 test[56851:22943615] ---任务23---<NSThread: 0x1d447b4c0>{number = 3, name = (null)}
2018-12-08 12:48:56.743445+0800 test[56851:22943615] ---任务24---<NSThread: 0x1d447b4c0>{number = 3, name = (null)}
2018-12-08 12:48:56.743463+0800 test[56851:22943615] ---任务25---<NSThread: 0x1d447b4c0>{number = 3, name = (null)}
2018-12-08 12:48:56.743480+0800 test[56851:22943615] ---任务3---<NSThread: 0x1d447b4c0>{number = 3, name = (null)}
2018-12-08 12:48:56.743496+0800 test[56851:22943615] ---任务4---<NSThread: 0x1d447b4c0>{number = 3, name = (null)}
2018-12-08 12:48:56.743513+0800 test[56851:22943615] ---任务5---<NSThread: 0x1d447b4c0>{number = 3, name = (null)}

2.Dispatch Group

在项目中会遇到这种需求,假设有四个任务,分别为任务A、任务B、任务C、任务D,其中任务D要等待任务A B C全部处理完了才可处理。如果 任务A B C都是串行执行,只需要使用一个 Serial Dispatch Queue,按照顺序把 任务 A B C依次放入队列中,最后追加任务D到队列中即可实现。但是如果 任务A B C不是串行执行的,而是并发执行的,该怎么实现这个功能呢。自己写代码用标志位来判断并发的任务A B C是否都执行完,再执行任务D也是可以实现的。但是比较复杂,这种情况下使用 Dispatch Group 就比较简单了。
参照代码如下

    dispatch_queue_t golbalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, golbalQueue, ^{
         NSLog(@"---任务A---%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, golbalQueue, ^{
        NSLog(@"---任务B---%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, golbalQueue, ^{
        NSLog(@"---任务C---%@", [NSThread currentThread]);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"---任务D---%@", [NSThread currentThread]);
    });

执行结果如下

2018-12-08 14:35:58.230566+0800 test[57002:22985593] ---任务A---<NSThread: 0x1d006e340>{number = 3, name = (null)}
2018-12-08 14:35:58.230700+0800 test[57002:22985588] ---任务C---<NSThread: 0x1d447a240>{number = 4, name = (null)}
2018-12-08 14:35:58.230746+0800 test[57002:22985593] ---任务B---<NSThread: 0x1d006e340>{number = 3, name = (null)}
2018-12-08 14:35:58.237903+0800 test[57002:22985488] ---任务D---<NSThread: 0x1d4069080>{number = 1, name = main}

因为向 Global Dispatch Queue 即 Concurent Dispatch Queue 追加处理,多个线程并行执行,所以任务A B C的执行顺序不定,但是任务D一定是最后执行的。

3.dispatch_barrier_async

在访问数据库和文件时,有读和写两种操作,如果使用 Serial Dispatch Queue 可避免数据竞争的问题。这样虽然数据竞争问题解决了,但这样做存在一个新的问题就是效率太低了。大家都知道,多个读操作并行执行是不存在数据竞争问题的,只是写入操作不能和其他写入操作以及包含读取操作的其他处理并行执行。
为了提高效率,可以这样做,读操作追加到 Concurrent Dispatch Queue 中,写操作在任一个读操作没有执行的状态下,追加到 Serial Dispatch Queue 中即可。这样虽可以实现,但是代码会很复杂。
为了更方便的解决上面问题,可以使用 dispatch_barrier_async 函数。假如现在有8个读操作,一个写操作,要求是先并行执行4个读操作,然后串行执行一个写操作,后面再并行执行后面4个读操作。
代码如下

    dispatch_queue_t queue = dispatch_queue_create("barrier_queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"---读操作1---");
    });
    dispatch_async(queue, ^{
        NSLog(@"---读操作2---");
    });
    dispatch_async(queue, ^{
        NSLog(@"---读操作3---");
    });
    dispatch_async(queue, ^{
        NSLog(@"---读操作4---");
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"------写操作1---");
    });
    dispatch_async(queue, ^{
        NSLog(@"---读操作5---");
    });
    dispatch_async(queue, ^{
        NSLog(@"---读操作6---");
    });
    dispatch_async(queue, ^{
        NSLog(@"---读操作7---");
    });
    dispatch_async(queue, ^{
        NSLog(@"---读操作8---");
    });

执行结果如下

2018-12-08 15:27:08.999895+0800 test[57067:23007593] ---读操作3---
2018-12-08 15:27:08.999936+0800 test[57067:23007593] ---读操作4---
2018-12-08 15:27:08.999959+0800 test[57067:23007592] ---读操作2---
2018-12-08 15:27:08.999974+0800 test[57067:23007591] ---读操作1---
2018-12-08 15:27:09.000043+0800 test[57067:23007592] ------写操作1---
2018-12-08 15:27:09.000074+0800 test[57067:23007592] ---读操作5---
2018-12-08 15:27:09.000101+0800 test[57067:23007593] ---读操作8---
2018-12-08 15:27:09.000130+0800 test[57067:23007590] ---读操作7---
2018-12-08 15:27:09.000149+0800 test[57067:23007591] ---读操作6---

多次执行,读操作1 2 3 4执行顺序不一样,读操作5 6 7 8执行顺序也不一样,但是写操作一定是在读操作1 2 3 4执行完再执行,读操作5 6 7 8也一定是在写操作执行完成后才执行。
dispatch_barrier_async 函数会等待追加到 Concurrent Dispatch Queue 上的并行执行的处理全部结束之后,再将指定的处理追加到该 Concurrent Dispatch Queue 中,然后在由 dispatch_barrier_async 函数追加的处理执行完毕后,Concurrent Dispatch Queue 才恢复为一般的动作,追加到该 Concurrent Dispatch Queue 的处理又开始并行执行。
注意:dispatch_barrier_async方法有一个隐藏的坑。当并行队列是 dispatch_get_global_queue 时,dispatch_barrier_async 方法失效了。dispatch_barrier_async 只在 dispatch_queue_create 方法创建出来的并行队列中有效。

4.dispatch_apply

dispatch_apply 函数按指定的次数将指定的Block追加到指定的 Dispatch Queue 中,并等待全部处理执行结束。
代码如下

    dispatch_queue_t golbalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(10, golbalQueue, ^(size_t index) {
        NSLog(@"---%zu---%@", index, [NSThread currentThread]);
    });
    NSLog(@"---done---");

执行结果如下

2018-12-08 16:14:15.121169+0800 test[57130:23027202] ---0---<NSThread: 0x1d4076400>{number = 1, name = main}
2018-12-08 16:14:15.121195+0800 test[57130:23027285] ---2---<NSThread: 0x1d007fec0>{number = 4, name = (null)}
2018-12-08 16:14:15.121221+0800 test[57130:23027202] ---6---<NSThread: 0x1d4076400>{number = 1, name = main}
2018-12-08 16:14:15.121228+0800 test[57130:23027285] ---7---<NSThread: 0x1d007fec0>{number = 4, name = (null)}
2018-12-08 16:14:15.121246+0800 test[57130:23027202] ---8---<NSThread: 0x1d4076400>{number = 1, name = main}
2018-12-08 16:14:15.121263+0800 test[57130:23027285] ---9---<NSThread: 0x1d007fec0>{number = 4, name = (null)}
2018-12-08 16:14:15.121278+0800 test[57130:23027286] ---1---<NSThread: 0x1d4473340>{number = 3, name = (null)}
2018-12-08 16:14:15.121292+0800 test[57130:23027287] ---3---<NSThread: 0x1d4475800>{number = 6, name = (null)}
2018-12-08 16:14:15.121320+0800 test[57130:23027288] ---4---<NSThread: 0x1c8070c80>{number = 5, name = (null)}
2018-12-08 16:14:15.121348+0800 test[57130:23027302] ---5---<NSThread: 0x1c0070e40>{number = 7, name = (null)}
2018-12-08 16:14:15.121361+0800 test[57130:23027202] ---done---

因为是在 Global Dispatch Queue 中并行执行,多次执行,打印0-9顺序可能不一样,但是---done---一定是在最后打印的。因为 dispatch_apply 函数会等待全部处理执行结束。
函数第一个参数是重复次数,第二个参数是追加对象的 Dispatch Queue,第三个参数是追加的处理Block。跟以前不一样的是,第三个参数的Block是带有参数的Block,这是为了按第一个参数重复追加Block并区分各个Block而使用。
下面有一个利用 dispatch_apply 遍历 NSArray 的列子

    NSArray *array = @[@"000", @"111", @"222", @"333", @"444", @"555", @"666", @"777", @"888", @"999"];
    dispatch_queue_t golbalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(golbalQueue, ^{
        dispatch_apply(array.count, golbalQueue, ^(size_t index) {
            NSLog(@"---%zu---%@", index, [array objectAtIndex:index]);
        });
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"---done---");
        });
    });

执行结果如下

2018-12-08 16:25:22.255896+0800 test[57134:23030709] ---2---222
2018-12-08 16:25:22.255885+0800 test[57134:23030708] ---0---000
2018-12-08 16:25:22.255884+0800 test[57134:23030571] ---1---111
2018-12-08 16:25:22.255939+0800 test[57134:23030708] ---3---333
2018-12-08 16:25:22.255941+0800 test[57134:23030571] ---4---444
2018-12-08 16:25:22.255950+0800 test[57134:23030708] ---5---555
2018-12-08 16:25:22.255954+0800 test[57134:23030571] ---6---666
2018-12-08 16:25:22.255961+0800 test[57134:23030708] ---7---777
2018-12-08 16:25:22.255964+0800 test[57134:23030571] ---8---888
2018-12-08 16:25:22.255971+0800 test[57134:23030708] ---9---999
2018-12-08 16:25:22.255999+0800 test[57134:23030571] ---done---

由于 dispatch_apply 函数与 dispatch_sync 函数相同,会等待处理执行结束,所以也要注意死锁问题。因此推荐在 dispatch_async 函数中非同步执行 dispatch_apply 函数。

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

推荐阅读更多精彩内容