2017年3月17日
GCD (Grand Central Dispatch) 是Apple公司开发的一种技术,它旨在优化多核环境中的并发操作并取代传统多线程的编程模式。
先从最简单的来学习。
串行 & 并行
GCD中dispatch_queue大致可以分为三类
- 全局的并行的queue
- 主线程的串行的queue
- 自定义的queue
dispatch_async 函数会将传入的block块放入指定的queue里运行。这个函数是异步的,这就意味着它会立即返回而不管block是否运行结束。因此,我们可以在block里运行各种耗时的操作(如网络请求) 而同时不会阻塞UI线程。
dispatch_get_global_queue 会获取一个全局队列,我们姑且理解为系统为我们开启的一些全局线程。我们用priority(优先)指定队列的优先级,而flag作为保留字段备用(一般为0)。
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
dispatch_get_global_queue 有两个参数,第一个参数的意思是带便优先级,一般为上面的四个值,但不是固定的。第二个参数是预留参数暂时没有什么鸟用。
dispatch_get_main_queue 会返回主队列,也就是UI队列。它一般用于在其它队列中异步完成了一些工作后,需要在UI队列中更新界面(比如上面代码中的[self updateUIWithResult:result])的情况。
这三个是比较基础的函数,下面上一些代码,结合着看
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task 1");
[NSThread sleepForTimeInterval:3];//延时3秒钟
NSLog(@"end task 1");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task 2");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 2");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"start task 3");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 3");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSLog(@"start task 1");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 1");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"start task 2");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 2");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start task 3");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 3");
});
所以,可以修改第一个参数,你来控制哪个线程先来执行,但是线程是并发执行的,所以你不能保证哪个线程会先执行完,也就是不能保证我们的耗时任务是按照顺序执行的。
所以,想要解决这个问题,那就用到自定义队列啦。
创建自定义队列就用到了这个方法:
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)。
里面也有两个参数,第一个参数是确定唯一queue的一个标识,第二个参数创建queue的类型,串行的还是并行的。
/**
* 我们创建了一个串行的queue
*
* @param "com.gcd.test.queue" 唯一标识这个queue
* @param DISPATCH_QUEUE_SERIAL 说明是串行的queue
*/
dispatch_queue_t myQueue = dispatch_queue_create("com.gcd.test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(myQueue, ^{
NSLog(@"start task 1");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 1");
});
dispatch_async(myQueue, ^{
NSLog(@"start task 2");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 2");
});
dispatch_async(myQueue, ^{
NSLog(@"start task 3");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 3");
});
可以通过上面的图片看到,42565是主线程的ID,1589679是异步线程的ID。上面我们不是都说是串行queue,所以就相当于是同一个异步线程,最开始上面的是并发queue,所以异步线程ID都不同。
dispatch_group_queue
在我们平时实际项目中经常会有这样的需求,就是在多个任务异步处理后我们需要一个统一的回调通知去处理接下来的业务,这个时候我们就想到了dispatch group了,当所有任务完成后会调用dispatch_group_notify
/**
* 创建一个并发的queue
*/
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd.queue", DISPATCH_QUEUE_CONCURRENT);
/**
* 创建一个group
*/
dispatch_group_t group = dispatch_group_create();
/**
* 执行3个耗时任务
*/
dispatch_group_async(group, queue, ^{
NSLog(@"start task 1");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"start task 2");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"start task 3");
[NSThread sleepForTimeInterval:3];
NSLog(@"end task 3");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"task over");
});
当三个异步耗时操作完成后,统一一个回调,两点注意:
回调回来的数据异步线程中,这一点通过它前边的线程ID号1631915就能发现。所以如果后续执行刷新UI操作需要到主线程中完成。
接收回调的这个线程就是任务最后执行完的那个线程,系统做了优化并没有多开一个线程来处理。
今天的学习记录,就这些,很晚了,晚安啦。