GCD(Grand_Central_Dispatch) 详解二

dispatch_sync

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

GCD(Grand_Central_Dispatch) 详解二_第1张图片
dispatch_async 函数的处理流程.png

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

GCD(Grand_Central_Dispatch) 详解二_第2张图片
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 多线程和内存管理)

你可能感兴趣的:(GCD(Grand_Central_Dispatch) 详解二)