一、GCD简介:
Grand Central Dispatch(GCD)是苹果特有的异步执行任务的技术,GCD用很简洁的记述方式,实现了复杂繁琐的多线程编程。
GCD代码抽象使用:
dispatch_async(queue,^{
//耗时操作
dispatch_async(dispatch_get_main_queue,^{
//主线程操作,如UI更新
});
});
二、Dispatch Queue 串行或者并行队列
GCD存在两种Dispatch Queue,一种是等待现在执行中处理的Serial Dispatch Queue(串行队列),另一种是不等待现在执行中处理的Concurrent Dispatch Queue(并行队列)。
一个Serial Queue里的任务是按照FIFO进行执行调度的,一个Concurrent Queue里的任务是并行进行调度的。
Serial Queue可以通过 dispatch_queue_t serialQueue = dispatch_queue_create("...",NULL);生成;
Serial Dispatch Queue用于使用一个线程串行执行任务避免多个任务竞争更新相同的数据资源。
(注意:应当限制Serial Dispatch Queue生成的数量,不能生成大量的队列,会十分消耗内存)
Concurrent Queue通过dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("...",DISPATCH_QUEUE_CONCURRENT);
(注意:生成的Dispatch Queue需要手动释放队列,dispatch_release(queue));
三、系统已提供的Queue--Main Dispatch Queue/Global Dispatch Queue
实际上,不需要特意生成Dispatch Queue,系统也会提供几个-Main Dispatch Queue和Global Dispatch Queue。
Main Dispatch Queue,是在主线程执行的Dispatch Queue,因为主线程只有一个,所以Main Dispatch Queue自然是Serial Dispatch Queue。
dispatch_queue_t mainQueue = dispatch_get_main_queue();
Global Dispatch Queue是所有程序都能使用的Concurrent Dispatch Queue。Global Dispatch Queue有4个执行优先级,分别为High Priority、Default Priority、Low Priority和Background Priority。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
两者合并使用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//并行执行操作
dispatch_async(dispatch_get_main_queue(),^{
//主线程执行的处理
});
});
四、多个api介绍
1.dispatch_set_target_queue:变更执行优先级;
2.dispatch_after:在指定时间追加处理到Dispatch Queue
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,3ull*NSEC_PER_SEC);
dispatch_after(time,dispatch_get_main_queue(),^{
NSLog(@"waited at least 3 seconds");
//因为Main Dispatch Queue在主线程的RunLoop中执行,所以在比如每隔1/60秒执行的RunLoop中
//Block最快在3秒后执行,最慢在3+1/60秒后执行,如果main queue有大量处理则可能延时更长时间
});
3.Dispatch Group
在追加到Dispatch Queue的多个处理全部结束后向执行结束处理,可用Dispatch Group。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEAULT,0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,queue,^{NSLog(@"AAAAA");});
dispatch_group_async(group,queue,^{NSLog(@"BBBBBB");});
dispatch_group_async(group,queue,^{NSLog(@"CCCCCC");});
dispatch_group_notify(group,dispatch_get_main_queue(),^{
NSLog(@"done!");
});
dispatch_release(group);
4.dispatch_barrier_async
在访问数据库或文件时,使用Serial Dispatch Queue避免数据竞争的问题。为了高效访问,读取处理添加到Concurrent Dispatch Queue,写处理添加到Serial Dispatch Queue。
GCD提供dispatch_barrier_async函数,解决文件读写操作的性能问题。
5.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");
结果:
4
1
0
3
.....
done
注意:因为是current queue 所以,处理执行时间不定。但输出结果中最后的done必定在最后的位置上。这是因为dispatch_apply函数会等待全部处理执行结束。
第一个参数是重复次数,第二个参数为追加对象的Dispatch Queue,第三个参数为追加的处理。这样不必一个一个编写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_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue,^{
//等待dispatch_apply处理结束
dispatch_apply([array count],queue^{
//并列处理NSArray对象的全部对象
NSLog(@"%zu:%@",index,[array objectAtIndex:index]);
});
//dispatch_apply 函数中处理全部执行结束
//在Main Dispatch Queue中非同步执行
dispatch_async(dispatch_get_main_queue(),^{
//主线程操作
})
});
6.dispatch_suspend/dispatch_resume
挂起恢复指定Dispatch Queue。
7.Dispatch Semaphore
更细粒度的信号量,防止并行队列的数据竞争产生的问题
网络请求并发处理
GCD中3个信号量有关的操作
dispatch_semaphore_create 信号量创建
dispatch_semaphore_signal 发送通知
dispatch_semaphore_wait 信号量等待
例子:
int main(int argc,const char*argv[]){
@autoreleasepool{
//创建信号量
__block dispatch_semaphore_t seem = dispatch_semaphore_create(0);
// 创建队列
dispatch_queue_t queue = dispatch_queue_create("testBlock",NULL);
dispatch_async(queue,^{
for(int i = 0;i<10;i++){
NSLog(@"i=%d",i);
}
//发送通知
dispatch_semaphore_signal(sem);
});
//信号量等待
dispatch_semaphore_wait(semi,DISPATCH_TIME_FOREVER);
for(int j = 0;j<20;j++){
NSLog(@"j=%d",j);
}
}
return 0;
}
8.dispatch_once
dispatch_once函数是保证在应用程序执行中只执行一次指定处理的API。下面这种经常出现的用来进行初始化的源代码可通过dispatch_once函数简化,单例模式经常使用。
static dispatch_once_t pred;
dispatch_once(&pred,^{
//初始化
});
9.Dispatch I/O
读取大文件时,如果将文件分成合适的大小并使用Global Dispatch Queue并列读取的话,应该会比一般的读取速度快不少。现今的输入/输出硬件已经可以做到一次使用多个线程并更快地并列读取了。能实现这功能的就是Dispatch I/O和Dispatch Data。
五、ARC、Blocks、GCD使用范例
1.实际使用一下ARC、Blocks和GCD。实现从指定URL下载数据,在另外的线程中解析该数据并在主线程中使用其解析结果的源代码如下。代码中穿插了注释加以解说。
NSString *url = @"http://images/1.jpg";
//开始异步网络下载
[ASyncURLConnection request:url completeBlock:^(NSData *data){
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue,^{
//在Global Dispatch Queue中对下载数据进行解析处理,不妨碍主线程可长时间处理
dispatch_async(dispatch_get_main_queue(),^{
//在Main Dispatch Queue中使用解析结果。对用户界面进行反映处理
});
});
} errorBlock:^(NSError *error){
//发生错误
}];