GCD概要
多线程编程
一个 CPU 一次只能执行一个命令,一个 CPU 执行的 CPU 命令列为一条无分叉路经,当这种路径存在多条时,即为“多线程”,使用多线程的程序可以在某个线程和其他线程之间反复多次进行上下文切换,就好像能并列执行多线程一样,在具有多 CPU 核的情况下,就是真正并行执行了
容易产生的问题:数据竞争,死锁,消耗大量内存
GCD 的 API
Dispatch Queue
dispatch_async(queue, ^{
//想执行的任务
})
Dispatch Queue 有两种:Serial Dispatch Queue 和 Concurrent Dispatch Queue,前者使用一个线程,后者使用多个线程,在不能改变执行的处理顺序或不想并行执行多个处理时使用 Serial Dispatch Queue
dispatch_queue_create
通过 GCD 的 API 生成 Dispatch Queue
虽然 Serial Dispatch Queue 同时只能追加一个处理,但可用函数生成任意多个Dispatch Queue,多个 Serial Dispatch Queue 可并行执行(可能造成内存消耗问题),只在为了避免数据竞争时使用 Serial Dispatch Queue
当想并行执行不发生数据竞争等问题的处理时,使用 Concurrent Dispatch Queue,不管生成多少,XUN 内核只使用有效管理的内存
//生成Serial Dispatch Queue 第一个参数为名字
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
//生成Concurrent Dispatch Queue
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
//执行
dispatch_async(myConcurrentDispatchQueue, ^{NSLog(@"haha");});
通过 dispatch_queue_create 生成的 Dispatch Queue 在使用结束后通过dispatch_release 释放
在 dispatch_async 函数中追加 Block 到 Dispatch Queue,并立即释放完全没有问题,因为 Block 持有了 Dispatch Queue,在 Block 执行结束后被释放
Main Dispatch Queue/Global Dispatch Queue
可以直接获取系统标准提供的 Dispatch Queue,前者是在主线程执行的 Serial Dispatch Queue,后者是 Concurrent Dispatch Queue
Global Dispatch Queue 有四个执行优先级:High Priority/Default Priority/Low Priority/Background Priority
对 Main Dispatch Queue 做 retain 和 release 不会起任何变化,也不会有问题
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/*
* 可并行执行的处理
*/
/*
* 在Main Dispatch Queue中执行Block
*/
dispatch_async(dispatch_get_main_queue(), ^{
/*
* 只能在主线程中执行的处理
*/
});
});
dispatch_set_target_queue
dispatch_queue_create 生成的 Dispatch Queue 都使用与默认优先级 Global Dispatch Queue 相同执行优先级的线程,而变更执行优先级要使用dispatch_set_target_queue
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);
第一个参数为指定要变更执行优先级的 Dispatch Queue,不可为 Main/Global Dispatch Queue
不仅可以变更执行优先级,还可以作为 Dispatch Queue 的执行阶层,如果在多个 Serial Dispatch Queue 中指定某一个,那么原本应并行执行的多个 Serial Dispatch Queue,在目标 Queue 上只能同时执行一个
dispatch_after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"waited at least three secs");
});
dispatch_after 在指定时间后追加处理到 Dispatch Queue(不是执行处理)
ull 是 C 语言的数值字面量,是显式表明类型时使用的字符串(表示“unsigned long long”)
dispatch_time_t 类型可使用 dispatch_time 或 dispatch_walltime 函数作成,dispatch_time 通常用于计算相对时间,dispatch_walltime 用于计算绝对时间
Dispatch Group
在追加到 Dispatch Queue 中的多个处理全部结束后想执行结束处理,只使用一个 Serial Dispatch Queue 时,可将结束处理追加到最后,但多个时使用 Group
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{NSLog(@"blk0");});
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"done");});
//blk1
//blk2
//blk0
//done
追加处理的执行顺序不定,但执行结果的 done 一定是最后输出的。
无论向什么样的 Dispatch Queue 追加处理,使用 Dispatch Group 都可监视这些处理执行的结束,一旦结束,就将结束的处理追加到 Dispatch Queue 中(不一定是同一个 Queue)
也可以使用 dispatch_group_wait 函数
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
第二个参数为等待时间,DISPATCH_TIME_FOREVER 意味着永久等待,如果 dispatch_group_wait 函数的返回值不为 0,意味着经过指定时间,Dispatch Group 的某一个处理还在执行,为 0 则全部执行结束,DISPATCH_TIME_FOREVER 的返回值恒为 0
等待意味着一旦调用 dispatch_group_wait,该函数就处于调用的状态而不返回,即执行该函数的线程停止
dispatch_barrier_async
该函数和 Concurrent Dispatch Queue 一起使用,dispatch_barrier_async 会等待追加到 Concurrent Dispatch Queue 上的并行执行的处理全部结束后,再将指定的处理追加到该 Concurrent Dispatch Queue 中,然后在该处理执行完毕后,Concurrent Dispatch Queue 才恢复为一般的动作,又开始并行执行
dispatch_barrier_sync(queue, blk_for_writing);
使用该函数可实现高效率的数据库访问和文件访问
dispatch_sync
意味着将 Block “同步”到指定的 Dispatch Queue 中,在追加 Block 结束之前,dispatch_sync 函数会一直等待,即当前线程停止
易造成死锁
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{NSLog(@"Hello");});
//Serial Dispatch Queue也会引起同样问题
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_async(queue, ^{
dispatch_sync(queue, ^{NSLog(@"Hello");});
});
dispatch_apply
dispatch_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");
各个处理的执行时间不定,但输出结果的 done 必定在最后的位置
推荐在 dispatch_async 函数中非同步地执行 dispatch_apply 函数
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
dispatch_apply([array count], queue, ^(size_t index) {
NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"done");
});
});
dispatch_suspend/dispatch_resume
希望不执行已追加的处理时可以挂起 Dispatch Queue
//挂起
dispatch_suspend(queue);
//恢复
dispatch_resume(queue);
Dispatch Semaphore
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for(int i = 0; i < 100000; ++i) {
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[array addObject: [NSNumber numberWithInt: i]];
dispatch_semaphore_signal(semaphore);
});
}
dispatch_release(semaphore);
dispatch_once
保证程序只执行一次处理,如进行初始化的代码可以通过 dispatch_once 简化
static int initialized = NO;
if(initialized == NO){
//初始化
initialized = YES;
}
//简化
static dispatch_once_t pred;
dispatch_once(&pred, ^{
//初始化
})
通过 dispatch_once 函数,即使在多线程环境下执行也可保证安全。这就是所说的单例模式,在生成单例对象时使用