GCD 中的常用函数
一. 什么是GCD
Grand Central Dispatch 简称(GCD)是苹果公司开发的异步执行技术之一,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统。这建立在任务并行执行的线程池模式的基础上的。
二. 常用函数的整理
- (void)viewDidLoad {
[super viewDidLoad];
#pragma mark - 导入GCD之前的简单多线程技术
/*
* NSObject performSelectorInBackground: withObject: 方法
* 执行后台线程
*/
[self performSelectorInBackground:@selector(doSomething) withObject:nil];
#pragma mark - GCD技术多线程的一般方法
dispatch_queue_t queue = dispatch_queue_create("XXXX", NULL);
/*
* 异步执行 dispatch_async
* 同步执行 dispatch_sync
*/
dispatch_async(queue, ^{
/*
* 需要长时间处理的任务
* 例如AR画像识别
* 例如数据库访问
* TODO:---------
*/
/*
* 长时间处理结束,主线程使用其处理结果
*/
dispatch_async(dispatch_get_main_queue(), ^{
/*
* 只在主线程可以的处理
* 例如用户界面更新
*/
});
});
#pragma mark - GCD队列获得方法
// 第一种方法
// 开发者要做的只是定义想执行的任务并追加到合适的Dispatch Queue
dispatch_async(queue, ^{
/*
* 想执行的任务
*/
});
// 生成Serial Dispatch Queue 的方法(串行队列)
dispatch_queue_t serialQueue = dispatch_queue_create("XXXXX", NULL);
// 生成Concurrent Dispatch Queue 的方法 (并行队列)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.ideal.gcd", DISPATCH_QUEUE_CONCURRENT);
// 启动创建的队列
dispatch_async(serialQueue, ^{
/*
* 想执行的任务
*/
});
dispatch_async(conCurrentQueue, ^{
/*
* 想执行的任务
*/
});
/*
* 在非ARC的情况下,需要释放创建的队列。
dispatch_release(queue);
dispatch_release(serialQueue);
dispatch_release(conCurrentQueue);
*/
// 第二种方法
// 获得系统提供的标准 Dispatch Queue
/*
Main Dispatch Queue :
主线程只有一个,因此Main Dispatch Queue 是Serial Dispatch Queue。
Global Dispatch Queue :
全局队列,是所有应用程序都能使用的Concurrent Dispatch Queue,因此没必要生成并行队列,只需获取系统的就可以了。
*/
// 系统提供Dispatch Queue 的获取方法
// Main Dispatch Queue :
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
// Global Dispatch Queue :
// 最高优先级:
dispatch_queue_t globalQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
// 默认优先级:
dispatch_queue_t globalQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 低优先级:
dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
// 后台优先级:
dispatch_queue_t globalQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 启动创建的队列
dispatch_async(mainDispatchQueue, ^{
/*
* 只能在主线程执行的任务
*/
});
dispatch_async(globalQueueHigh, ^{
/*
* 可并行执行的任务
*/
});
dispatch_async(globalQueueDefault, ^{
/*
* 可并行执行的任务
*/
});
dispatch_async(globalQueueLow, ^{
/*
* 可并行执行的任务
*/
});
dispatch_async(globalQueueBackground, ^{
/*
* 可并行执行的任务
*/
});
#pragma mark - 变更队列优先级方法:dispatch_set_target_queue
// 使用 dispatch_queue_create 创建的队列优先级都是默认优先级
// dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
// 第一个参数:需要变更优先级的队列;第二个参数:提供变更优先级的队列
dispatch_set_target_queue(queue, globalQueueBackground);
/*
在必须将不可并行执行的处理追加到多个Serial Dispatch Queue 中时,如果使用
dispatch_set_target_queue函数将目标指定为某一个Serial Dispatch Queue ,
即可防止处理并行处理。
*/
#pragma mark - 设置处理开始时间:dispatch_after
// 在指定时间后开始执行处理
// dispatch_after(dispatch_time_t when, dispatch_queue_t queue, Block block);
// 第一个参数是指定时间,第二个参数是想要追加处理的队列,第三个参数是需要执行的block块;
// 以下源代码表示从现在开始3秒后 dispatch_time_t 类型
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
/*
* 在主线程执行的任务
*/
});
// 注意:dispatch_after并不是在指定时间后开执行,而是追加处理到Dispatch Queue。
// NSEC_PER_MSEC 以毫秒为单位,NSEC_PER_SEC 以秒为单位。
// dispatch_time 通常用于计算相对时间,dispatch_walltime函数用于计算绝对时间
// 由NSDate类获取 dispatch_time 时间
/*
dispatch_time_t getDispatchTimeByDate(NSDate *date)
{
NSTimeInterval interval;
double second ,subSecond;
struct timespec time ;
dispatch_time_t milestone;
interval = [date timeIntervalSince1970];
subSecond = modf(interval, &second);
time.tv_sec = second;
time.tv_nsec = subSecond * NSEC_PER_MSEC;
milestone = dispatch_walltime(&time, 0);
return mileStone;
}
*/
#pragma mark - 分发队列的组:Dispatch Group
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, globalQueue, ^{
NSLog(@"testOne");
});
dispatch_group_async(group, globalQueue, ^{
NSLog(@"testTwo");
});
dispatch_group_async(group, globalQueue, ^{
NSLog(@"testThree");
});
// 第一种:在执行处理结束后通知另一队列执行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
// dispatch_release(group);//非ARC时使用
// 添加Group后,只有在Group中的队列执行完之后,才会执行主程序队列。
// 第二种:等待全部处理结束
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// dispatch_release(group);//非ARC时使用
// 指定等待间隔为1秒时如下:
dispatch_time_t time_One = dispatch_time(DISPATCH_TIME_NOW,1ull * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time_One);
if (result == 0) {
/*
*属于Dispatch Group 的全部处理执行结束
*/
}else{
/*
*属于Dispatch Group 的某个处理还在进行中
*/
}
#pragma mark - dispatch_barrier_async 函数的使用
/*
访问数据库或文件时,使用Serial Dispatch Queue可避免数据竞争问题。写入处理不可与其他写入处理
以及包含读取处理的其他某些处理并行执行,但是如果读取处理只是与读取处理并行执行,多个并行执行就
不会发生问题。
*为了搞笑的进行访问,读取处理追加到Concurrent Dispatch Queue 中,写入处理在任一个读取处理
没有执行的状态下,追加到Serial Dispatch Queue 中即可。
第一种方法:利用Dispatch Group 和 dispatch_set_target_queue 函数实现(实现较复杂);
第二种方法:利用dispatch_barrier_async 函数,与dispatch_queue_create函数生成的
Concurrent Dispatch Queue 一起使用
使用第二种方法可实现高效的数据库访问和文件访问
*/
dispatch_queue_t queueTwo = dispatch_queue_create("XXX", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queueTwo, ^{
NSLog(@"1-reading");
});
dispatch_async(queueTwo, ^{
NSLog(@"2-reading");
});
dispatch_async(queueTwo, ^{
NSLog(@"3-reading");
});
dispatch_async(queueTwo, ^{
NSLog(@"4-reading");
});
// dispatch_release(queueTwo);//非ARC时使用
// 错误写法:
dispatch_async(queueTwo, ^{
NSLog(@"1-reading");
});
dispatch_async(queueTwo, ^{
NSLog(@"2-reading");
});
/*
*写入处理
*
*将写入的内容读取之后的处理中
*/
dispatch_async(queueTwo, ^{
NSLog(@"1-writing");
});
dispatch_async(queueTwo, ^{
NSLog(@"3-reading");
});
dispatch_async(queueTwo, ^{
NSLog(@"4-reading");
});
//上述的做法可能导致读取出不符的数据,还可能因非法访问导致App异常结束,如果添加多个写入操作,
//问题更多,如数据竞争
// 正确写法:
dispatch_async(queueTwo, ^{
NSLog(@"1-reading");
});
dispatch_async(queueTwo, ^{
NSLog(@"2-reading");
});
/*
*写入处理
*
*将写入的内容读取之后的处理中
*/
dispatch_barrier_async(queueTwo, ^{
NSLog(@"1-writing");
});
dispatch_async(queueTwo, ^{
NSLog(@"3-reading");
});
dispatch_async(queueTwo, ^{
NSLog(@"4-reading");
});
//dispatch_barrier_async 函数会等待追加到Concurrent Dispatch Queue上得并行处理全部结束之后,
//再将指定的处理追加到该Concurrent Dispatch Queue中。然后由dispatch_barrier_async
//函数追加的处理执行完毕后,Concurrent Dispatch Queue 才恢复一般的动作,追加到
//Concurrent Dispatch Queue的处理有开始并行执行。
#pragma mark - 非同步dispatch_async与同步dispatch_sync
/*
*非同步(dispatch_async):将指定的block任务非同步的追加到指定的Dispatch Queue中,
dispatch_async 函数不做任何等待。不等待处理执行结束。
*同步(dispatch_sync):将指定的block任务同步追加到指定的Dispatch Queue 中,在追加的block
任务结束之前,dispatch_sync 函数会一直等待。
dispatch_sync 函数的使用场景:执行某个 Dispatch Queue 时,使用另外的线程进行处理,
处理结束后立即使用处理的结果。
注意:一旦调用dispatch_sync 函数,在指定的处理执行结束之前该函数不会返回。dispatch_sync
函数可简化源代码,可以说是简易版的dispatch_group_wait 函数。
正因为dispatch_sync函数使用简单,所以容易引起死锁。
*/
// 引起死锁的写法:
dispatch_queue_t queueThree = dispatch_get_main_queue();
dispatch_sync(queueThree, ^{
NSLog(@"dead");
});
dispatch_async(queueThree, ^{
dispatch_sync(queueThree, ^{
NSLog(@"dead");
});
});
// Serial Dispatch Queue 也会引起死锁
dispatch_queue_t queueFour = dispatch_queue_create("XXXXX", NULL);
dispatch_async(queueFour, ^{
dispatch_sync(queueFour, ^{
NSLog(@"dead");
});
});
/*
由dispatch_barrier_async 函数中含有async可以推测出,相应地也有dispatch_barrier_sync函数,
dispatch_barrier_async 函数的作用是在等待追加的处理全部执行结束后,再追加处理到Dispatch Queue中,
它还与dispatch_sync 函数相同,会等待追加处理的执行结束。
*/
#pragma mark - dispatch_apply 函数
/*
dispatch_apply 函数是dispatch_sync 函数和 Dispatch Group 的关联API。
该函数按指定的次数将指定的block追加到指定的Dispatch Queue中,并等待全部处理执行结束。
推荐在dispatch_async 函数中使用dispatch_apply
*/
dispatch_queue_t queueFive = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queueFive, ^(size_t index) {
NSLog(@"%zu",index);
});
NSLog(@"done");
NSArray *array = @[@"one",@"two",@"three",@"four"];
dispatch_apply([array count], queueFive, ^(size_t index) {
NSLog(@"%zu---%@",index,[array objectAtIndex:index]);
});
NSLog(@"done");
// 实例一:
dispatch_queue_t queueSix = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*
*在Global Dispatch Queue 中非同步执行
*/
dispatch_async(queueSix, ^{
/*
*Global Dispatch Queue
*等待 dispatch_apply 函数中全部处理执行结束
*/
dispatch_apply([array count], queueSix, ^(size_t index) {
/*
*并列处理包含在NSArray对象的全部对象
*/
NSLog(@"%zu---%@",index,[array objectAtIndex:index]);
});
/*
*dispatch_apply 函数中的处理全部执行结束
*/
/*
*在Main Dispatch Queue 中非同步执行
*/
dispatch_async(dispatch_get_main_queue(), ^{
/*
*在Main Dispatch Queue 中执行处理
*用户界面更新
*/
NSLog(@"done");
});
});
#pragma mark - 暂时挂起分发队列 dispatch_suspend/dispatch_resume
/*当追加大量处理到Dispatch Queue 时,再追加处理的过程中,有时希望不执行已追加的处理,
例如演算结果被block截获时,一些处理会对这个演算结果造成影响。在次情况下,只要挂起Dispatch Queue
即可,可以执行时在恢复。
*/
// dispatch_suspend 函数挂起指定的Dispatch Queue
dispatch_suspend(queue);
// dispatch_resume 函数恢复指定的Dispatch Queue
dispatch_resume(queue);
// 这些函数对已经执行的处理没有影响。挂起后,追加到Dispatch Queue 中但尚未执行的处理在此之后停止执行,
// 而恢复则使得这些处理能继续执行。以上函数仅对自定义的队列有用,对于系统的标准队列无用。
#pragma mark - 更细粒度的排他控制函数 Dispatch Semaphore
/*
由于当并行执行的处理更新数据时,会产生数据不一致的情况,有时app还会异常结束。
虽然使用Serial Dispatch Queue 和 dispatch_barrier_async 函数可避免这类问题,但有必要
执行更细粒度的排他控制。
*/
dispatch_queue_t queueSeven = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSMutableArray *mArray = [NSMutableArray new];
for (int i = 0; i< 100; ++i) {
dispatch_async(queueSeven, ^{
[mArray addObject:[NSNumber numberWithInt:i]];
});
}
/*
以上代码,使用Global Dispatch Queue 更新NSMutableArray类对象,因此执行后由内存错误导致app
异常结束的概率很高。
Dispatch Semaphore 是持有计数的信号,该计数是多线程编程中得计数类型信号。Dispatch Semaphore
中使用计数实现功能,计数为0时等待,计数为1或大于1时,减去1而不等待。
创建semaphore
*/
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
/*
dispatch_semaphore_wait 函数等待Dispatch Semaphore 的计数值大于或等于1,
当计数值大于或等于1,或者在待机中计数值大于等于1时,对计数进行减法并从dispatch_semaphore_wait
返回。第二个参数与dispatch_group_wait函数相同,由dispatch_time_t 类型值指定等待时间。
同时,dispatch_semaphore_wait 函数返回值与dispatch_group_wait 函数相同,也可以通过
返回值进行处理。
*/
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 实例一:
dispatch_time_t semTime = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
long semResult = dispatch_semaphore_wait(semaphore, semTime);
if (semResult) {
/*
*由于dispatch_semaphore 的计数值达到大于等于1
*或者在待机中的指定时间内
*dispatch_semaphore 的计数值达到大于等于1
*所欲dispatch_semaphore 的计数值减去1
*可执行需要进行排他控制的处理
*/
}else {
/*
*由于dispatch_semaphore 的计数值为0
*因此在到达指定时间为止待机
*/
}
// dispatch_semaphore_wait 函数返回0时,可安全的执行需要进行排他控制的处理,该处理结束时,
// 通过dispatch_semaphore_signal 函数将dispatch_semaphore 计数值加1.
dispatch_queue_t queueEight = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*
生成dispatch_semaphore
dispatch_semaphore的计数初始值设置为“1”。
保证可访问 NSMutableArray类对象的线程同时只能有一个。
*/
dispatch_semaphore_t semaphoreOne = dispatch_semaphore_create(1);
NSMutableArray *mArrayOne = [[NSMutableArray alloc]init];
for (int i = 0; i< 1000; ++i) {
dispatch_async(queueEight, ^{
// 等待dispatch_semaphore
// 一直等待,直到dispatch_semaphore的计数值大于等于1.
dispatch_semaphore_wait(semaphoreOne, DISPATCH_TIME_FOREVER);
/*
由于dispatch_semaphore 的计数值达到大于等于1
所以将dispatch_semaphore 的计数值减去1
dispatch_semaphore_wait 函数执行返回。
即执行到此时的dispatch_semaphore 计数值恒为0,
由于可访问NSMutableArray 类对象的线程只有一个,因此可以安全的进行更新
*/
[mArrayOne addObject:[NSNumber numberWithInt:i]];
/*
排他处理结束,所以通过dispatch_semaphore_signal 函数
将dispatch_semaphore 的计数值加1
如果有通过dispatch_semaphore_wait 函数,等待dispatch_semaphore
的计数值增加的线程,就有最先等待的线程执行。
*/
dispatch_semaphore_signal(semaphoreOne);
});
}
/*
如果使用结束需要如下这样释放dispatch_semaphore
dispatch_release(semaphoreOne);
注意:在没有Serial Dispatch Queue 和 dispatch_barrier_async 函数那么大粒度且
一部分处理需要进行排他控制的情况下,Dispatch Semaphore 便可发挥威力。
*/
#pragma mark - dispatch_once 函数的使用
// dispatch_once 函数是保证在APP执行中,只执行一次指定处理的API。
static int initialized = NO;
if (initialized == NO) {
// 初始化
initialized = YES;
}
// 如果使用dispatch_once函数则如下
static dispatch_once_t pred;
dispatch_once(&pred, ^{
// 初始化
});
/*
上述 dispatch_once 函数,即使在多线程环境下执行,也可保证100%安全;
而第一种写法在多核CPU中,有可能多次执行初始化;
单例模式
*/
#pragma mark - Dispatch I/O 和 Dispatch Data
// 使用Dispatch I/O 和 Dispatch Data 可实现使用多个线程并列读取文件,
#pragma mark - GCD 的实现
/*
*/
}
#pragma mark - 处理方法的回调
/*
* 后台线程处理方法 doSomething
*/
-(void)doSomething
{
/*
* 需要长时间处理的任务
* 例如AR画像识别
* 例如数据库访问
*/
/*
* 长时间处理结束,主线程使用其处理结果
*/
[self performSelectorOnMainThread:@selector(doneSomething) withObject:nil waitUntilDone:NO];
}
/*
* 主线程处理方法 doneSomething
*/
-(void)doneSomething
{
/*
* 只在主线程可以的处理
* 例如用户界面更新
*/
}
本文以iOS高阶教程为蓝本,整理收集的GCD一些常用函数,方便自己记忆和翻阅!