dispatch_sync
dispatch_async 函数 “async”意味着 “非同步”(asynchronous), 将指定的 Block “非同步”的追加到指定的 Dispatch Queue 中。 dispatch_async 函数不做任何等待。
dispatch_sync 函数 “sync” 意味着 “synchronous”,也就是将指定的 block "同步” 追加到 指定的 Disaptch Queue 中,在追加结束之前, dispatch_sync 函数会一直等待。
假设使用一种情况:执行 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 函数初始化就不必担心这样的问题,这就是所说的单例子模式,在生成单例对象时候使用。