GCD(Grand_Central_Dispatch) 详解二

dispatch_sync

dispatch_async 函数 “async”意味着 “非同步”(asynchronous), 将指定的 Block “非同步”的追加到指定的 Dispatch Queue 中。 dispatch_async 函数不做任何等待。

dispatch_async 函数的处理流程.png

dispatch_sync 函数 “sync” 意味着 “synchronous”,也就是将指定的 block "同步” 追加到 指定的 Disaptch Queue 中,在追加结束之前, dispatch_sync 函数会一直等待。

dispatch_sync函数.png

假设使用一种情况:执行 Main Dispatch Queue 时候, 使用另外的线程 Global Dispatch Queue 进行处理, 处理结束后立即使用所得到的结果,在这种情况袭就要使用 dispatch_sync 函数,

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
   
   dispatch_sync(queue, ^{/* 处理 */});
  
可以说是简易版的 dispatch_group_wait 函数。但容易造成问题,如死锁等。

   dispatch_queue_t queue = dispatch_get_main_queue();
   dispatch_sync(queue, ^{ /* 一些操作 */ });
   
该源码在 Main Dispatch Queue 即主线程中执行指定的Block, 并且等待其执行结果,而其实在主线程中正在执行这些源代码,无法执行追加到Main Dispatch Queue 的 Block。

dispatch_apply

disaptch_apply 函数是 dispatch_sync 函数和 Dispatch Group 的关联 API。该函数按照指定的次数将指定的 block 追加到指定的 Dispatch Queue中, 并等待全部处理执行结束。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue ^(size_t index)){

NSLog(@"%zu", index);
}
NSLog(@"%done");

执行结果
4,1,0,3,5,2,6,8,9,7, done,

// 第一个参数是 重复次数, 第二个参数为 追加对象的 Dispatch Queue,
   第三个参数为追加的处理。

因为在 Global Dispatch Queue 中执行处理, 所以各个处理的执行时间不定,但是输出结果中最后的 done 必定在最后的位置上,这是因为 dispatch_apply 函数会等待处理执行结束。
这样比如,对NSArray 类对象的所有元素执行处理时,不必一个个编写 for 循环了。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t index) {
NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
});

另外, 由于 dispatch_apply 函数也与 dispatch_sync 函数相同, 会等待处理执行结束, 因此推荐在 dispatch_async 函数中非同步执行 dispatch_apply 函数。

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); 
/*
 * 在 Global Dispatch Queu 中非同步执行
 */
dispatch_async(queue, ^{
/*
 * Global Dispatch Queue 等待 dispatch_appy 函数中全部处理执行结束
 */

   dispatch_apply([array count], queue, ^(size_t index){

   // 并列处理包含 NSArray 对象的全部对象
   
   });
   //
   dispatch_apply 函数中处理全部执行结束
})

dispatch_suspend / dispatch_resume

当追加大量处理到 Dispatch Queue 时候, 追加处理的过程中, 有时候希望不执行。已追加到的处理, 例如盐酸结果被 Block 截获时,一些处理会对演算结果造成影响。例如 演算结果被 block 截获时,一些处理会对这个演算结果造成影响。这种情况下, 只要挂起 Dispatch Queue 即可,当可以执行时候,在恢复。

dispatch_suspend 函数挂起指定的 Dispatch Queue
dispatch_resume 函数用于恢复指定的 Dispatch Queue。

这些函数对已经执行的处理没有影响,挂起之后, 追加到 Dispatch Queue 中,但是尚未执行的处理在吃之后停止执行,而回复则使得这些处理能够继续执行。

  dispathc_suspend(queue), 挂起线程
  dispatch_resume(queue) , 恢复线程

Dispatch Semaphore

当并行执行的处理更新数据时候, 会产生数据不一致的情况,有时候应用程序还会异常结束, 虽然使用了 Serial Dispatch Queue 和 dispatch_barrier_async 函数可以避免此类问题。当也不确保万一。
比如不考虑顺序, 将所有的数据追加到 NSMutableArray 中。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
NSMutableArray *array = [NSMutableArray array];

for (int i = 0; i< 1000; i++) {
dispatch_async(queue, ^{
 [array addObject:[NSNUmber numberWithInt:i]];
});
}

以上代码执行后,当 循环足够多的时候, 由于内存错误导致的程序异常结束的概率还挺高。此时应该使用 Dispatch Semaphore 进行说明。
Dispatch Semaphore 是持有计数的信号, 该技术是多线程编程中的技术类型信号,所谓信号,类似于过马路时候常用的手旗。可以通过时候,举旗,不可通过的时候,放下旗子。计数为0的时候, 表示等待,计数为1的或者大于1时候, 减去1而不等待。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

参数表示计数的初始值, 本例将计数值初始值为“1”。

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

dispatch_semaphore_wait 函数等待 Dispatch Semaphore的计数值达到大于或者等于 1 时,或者在待机中计数大于或等于1时, 对该计数进行减法并且从 dispatch_semaphore_wait 函数返回, 第二个参数与 dispatch_group_wait 函数等相同,由于 dispatch_time_t 类型值指定等待时间,该例子的参数为永久等待, 另外 dispatch_semaphore_wait 函数的返回值也与 dispatch_group_wait 相同。 如下代码。

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
long result = dispatch_semaphore_wait(semaphore, time);
if(result == 0){

//由于 Dispatch Semaphore的计数值达到大于等于1 或者 在待机中的指定时间内 Dispatch Semaphore 的计数值达到大于等于1 所以 Dispatch Semaphore 的计数值减去1。 
// 可执行需要进行排他控制的处理
/*
 * 由于 dispatch semaphore 的计数值为0;
 * 因此在答道指定时间为止待机。
 */
}

dispatch_semaphore_wait 函数返回0时,可返券执行排他控制的处理, 该处理结束时,通过dispatch_semaphore_signal 函数将 Dispatch Semaphore 的计数值加 1;

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

NSMutableArray *array = [NSMutableArray array];
for(int i = 0; i < 1000; i++){
// 等待 Dispatch Semaphore, 一直等待,直到 Dispatch Semaphore 的计数达到大于等于1

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 由于 Dispatch Semaphore 的计数达到大于等于 1;
所以将 Dispatch Semaphore 的计数值减去 1,
dispatch_semephore_wait 函数执行返回。
 // 即执行到此时的
 // Dispatch Semaphore 的计数为“0”;
 // 由于可访问 NSMutableArray 类对象的线程只有一个,
 // 因此可以安全的进行更新
 [array addObject:[NSNumber numberWithInt:i]];
 
  // 排他控制处理结束,所以通过    dispatch_semaphre_signal 函数
  // 将 Dispatch Semaphore 的计数增加1;
  // 如果有通过 dispatch_semaphore_wait 函数 Dispatch Semephore 的计数增加的线程, 就由最先等待的线程执行。
   
   dispatch_semaphore_signal(semaphore);
});
}

在没有 Serial Dispatch Queue 和 dispatch_barrier_async 函数那么大粒度且一部分处理需要进行排他控制的情况下,Dispatch Semaphore 便可发挥威力。

dispatch_once

dispatch_once 函数是抱枕在应用执行中只执行一次指定处理的 API,下面这种经常出现的用来进行初始化的源代码可通过 dispatch_once 函数来简化。

 static int initalized = NO;
 if(initalized == NO) {
  /*
   *  初始化
   */
   initalized = YES;
 }
 
如果使用 dispatch_once 函数, 则源代码写为
 
  static dispatch_once_t pred;
  dispatch_once(&pred, ^{
  
  /*
   * 初始化
   */
  
  });

看起来没太大变化, 但是通过 dispatch_once 函数, 该代码
即使是在多线程环境下执行,也可以保证百分百安全。

之前的源代码的绝大数情况下是安全的。但是在多核CPU中,在正在更新表示是否初始化的标志变量时读取,就有可能多次执行初始化处理。而用 dispatch_once 函数初始化就不必担心这样的问题,这就是所说的单例子模式,在生成单例对象时候使用。

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

推荐阅读更多精彩内容