关于GCD(Grand Central Dispatch):
苹果官方对GCD是这样说明的:开发者要做的只是定义想执行的任务并追加到适当的Dispatch Queue中。Dispatch Queue是执行处理的等待队列,我们可以通过dispatch_async等API,在block语法中记述想要执行的处理并将其追加到Dispatch Queue中,Dispatch Queue是按照追加的顺序 进行处理,先进先出FIFO。
其实就是帮助我们使用和管理线程。使用GCD,线程完全由系统管理,我们不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的调度队列(dispatch queue)。GCD会负责创建线程和调度你的任务,系统直接提供线程管理。
在学习使用GCD之前首先了解一些GCD的几个关键词,(因为GCD是基于C语言的,看起来有点怪)
dispatch:派遣 queue:队列 sync: 同步 async: 异步 DISPATCH_QUEUE_SERIAL:创建队列时候的一个宏,传这个参数意味着创建一个串行队列 (也可以传NULL) DISPATCH_QUEUE_CONCURRENT:创建队列时候的一个宏,传这个参数意味着创建一个并行队列
GCD的重要概念
队列
串行队列(串行队列,将任务一个一 个的执行,一个执行完才能执行另外一个)
并行队列 (并发执行,同一时间将所有任务拿出来,交给系统执行, 每一个任务的执行是无序的,执行的速度也不确定,一切都是GCD 来管理)
任务
异步执行 (使用异步执行,GCD会为我们开辟新的线程,不会卡死主线程,大部分的情况下我们是使用异步线程来处理比较复杂、耗时的任务。主线程留着刷新UI)
-
同步执行 (注意,同步执行系统是不会开辟新的线程的,同步只会在当前的线程中执行。)
总结一下:队列负责调度线程(串行和并行),线程负责执行任务,任务的执行有异步和同步(决定是否要开辟线程,异步会开辟新线程,同步会在当前的线程中执行,也就是立即执行)
了解了GCD的一些基本概念,下面开始使用GCD
并行队列
- 同步执行sync
NSLog(@"当前线程:%@ 开始",[NSThread currentThread]);
//创建并行队列 同步执行
//参数解释 第一个是这个队列的标识,其实是有一定的命名规范的,但我发现谁便写好像也没什么影响。
//第二个是决定了这个队列是并行还是串行。
//dispatch_sync 执行一个同步队列
dispatch_queue_t queue = dispatch_queue_create("test1", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"当前线程:%@ 执行任务",[NSThread currentThread]);
});
NSLog(@"当前线程:%@ 完成",[NSThread currentThread]);
/**
打印结果
当前线程:{number = 1, name = main} 开始
当前线程:{number = 1, name = main} 执行任务
当前线程:{number = 1, name = main} 完成
*/
//看完打印结果我们知道,任务是有序执行。因为就算系统把所有任务都拿出来,但是只有一个线程在干活,所以任务只能是一个一个执行。
- 异步执行async
//创建并行队列 异步执行
dispatch_queue_t queue = dispatch_queue_create("test2", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"当前线程%@ 开始",[NSThread currentThread]);
});
NSLog(@"当前线程%@ 完成",[NSThread currentThread]);
dispatch_queue_t queue2 = dispatch_queue_create("test3", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue2, ^{
NSLog(@"当前线程%@ 结束",[NSThread currentThread]);
});
//部分打印结果
/**
2017-01-12 16:55:49.656117 GCDDemo[8638:2183843] 当前线程{number = 1, name = main} 完成
2017-01-12 16:55:49.657470 GCDDemo[8638:2183879] 当前线程{number = 3, name = (null)} 开始
2017-01-12 16:55:49.658787 GCDDemo[8638:2183879] 当前线程{number = 3, name = (null)} 结束
2017-01-12 16:55:49.806684 GCDDemo[8638:2183843] 当前线程{number = 1, name = main} 完成
2017-01-12 16:55:49.810146 GCDDemo[8638:2183880] 当前线程{number = 5, name = (null)} 结束
2017-01-12 16:55:49.810285 GCDDemo[8638:2183877] 当前线程{number = 4, name = (null)} 开始
2017-01-12 16:55:50.009678 GCDDemo[8638:2183843] 当前线程{number = 1, name = main} 完成
2017-01-12 16:55:50.013130 GCDDemo[8638:2183877] 当前线程{number = 4, name = (null)} 开始
2017-01-12 16:55:50.013342 GCDDemo[8638:2183877] 当前线程{number = 4, name = (null)} 结束
*/
//结果是无序的,异步执行会开辟很多个线程(具体有多少个由系统决定)同时执行
串行队列
- 同步执行sync
//创建串行队列 同步执行
dispatch_queue_t queue = dispatch_queue_create("test4", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"当前线程%@ 开始",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"当前线程%@ 完成",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"当前线程%@ 结束",[NSThread currentThread]);
});
/**
2017-01-12 17:04:19.623236 GCDDemo[8642:2185860] 当前线程{number = 1, name = main} 开始
2017-01-12 17:04:19.623611 GCDDemo[8642:2185860] 当前线程{number = 1, name = main} 完成
2017-01-12 17:04:19.623808 GCDDemo[8642:2185860] 当前线程{number = 1, name = main} 结束
*/
//结果是有序执行,同步不会创建线程,会在当前线程立即执行,所以任务的执行顺序就是当前代码的先后顺序,写了和没有写是一个样。
- 异步执行async
//创建串行队列 异步执行
NSLog(@"当前线程:%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("test55", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"当前线程:%@ %d",[NSThread currentThread],i);
});
}
/**
2017-01-12 20:45:23.616 画图demo[5745:550177] 当前线程:{number = 1, name = main}
2017-01-12 20:45:23.619 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 0
2017-01-12 20:45:23.620 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 1
2017-01-12 20:45:23.620 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 2
2017-01-12 20:45:23.620 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 3
2017-01-12 20:45:23.621 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 4
2017-01-12 20:45:23.621 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 5
2017-01-12 20:45:23.621 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 6
2017-01-12 20:45:23.621 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 7
2017-01-12 20:45:23.622 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 8
2017-01-12 20:45:23.622 画图demo[5745:550290] 当前线程:{number = 2, name = (null)} 9
*/
//结果是有序执行,由于串行队列是一个任务完成后才开始另外一个任务,所以这里只会开辟一个子线程。
总结一下:
串行队列同步执行:综合上面阐述的串行队列的特点 --- 按顺序执行,同步:不会开启新的线程,则串行队列同步执行只是按部就班的一个一个执行。
串行队列异步执行:虽然队列中存放的是异步执行的任务,但是结合串行队列的特点,前一个任务不执行完毕,队列不会调度,所以串行队列异步执行也是一个一个的执行
并行队列同步执行:结合上面阐述的并行队列的特点,和同步执行的特点,可以明确的分析出来,虽然并行队列可以不需等待前一个任务执行完毕就可调度下一个任务,但是任务同步执行不会开启新的线程,所以任务也是一个一个的执行
并行队列异步执行:再上一条中说明了并行队列的特点,而异步执行是任务可以开启新的线程,所以这中组合可以实现任务的并发,再实际开发中也是经常会用到的
GCD - 主队列
主队列:专门负责在主线程上调度任务,不会在子线程上调度任务,在主队列不允许开辟新线程。
程序一启动主队列就会被创建。
//主队列异步执行
NSLog(@"1");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"4");
});
NSLog(@"2");
NSLog(@"3");
/**
2017-01-13 17:46:52.830 Block练习[13252:23530517] 1
2017-01-13 17:46:52.831 Block练习[13252:23530517] 2
2017-01-13 17:46:52.831 Block练习[13252:23530517] 3
2017-01-13 17:46:52.832 Block练习[13252:23530517] 4
*/
//解释一下为什么不是1 4 2 3 来执行。因为主队列,会要求主线程把所有的任务执行完,才会把主队列里面的任务调度给主线程。
//主队列同步执行
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"4");
});
NSLog(@"2");
NSLog(@"3");
/**
2017-01-13 17:52:58.468 Block练习[13329:23550381] 1
*/
/**结果是打印完1后就死掉了,也就是死锁。因为主队列是要求主线程把主线程的任务执行完才能开始执行主队列的任务。但是由于同步任务会立即在当前线程中执行,当主线程碰到 dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"4");
});
这个代码的时候,会想要立即执行里面的代码,但是主队列要求主线程把后面的代码执行完后才能执行,所以就冲突了。
*/
GCD - 全局队列
由于并发队列在开发过程中使用率是很高的,苹果索性就直接给我们提供了一个全局队列(实质上是并发队列),我们可以直接获取。
//获取全局队列
/**
第一个参数:队列的优先级(iOS8以前的叫法),iOS8后起了一个比较洋气的名字,叫服务质量,
long identifier:ios 8.0 告诉队列执行任务的“服务质量 quality of service”,系统提供的参数有:
QOS_CLASS_USER_INTERACTIVE 0x21, 用户交互(希望尽快完成,用户对结果很期望,不要放太耗时操作)
QOS_CLASS_USER_INITIATED 0x19, 用户期望(不要放太耗时操作)
QOS_CLASS_DEFAULT 0x15, 默认(不是给程序员使用的,用来重置对列使用的)
QOS_CLASS_UTILITY 0x11, 实用工具(耗时操作,可以使用这个选项)
QOS_CLASS_BACKGROUND 0x09, 后台
QOS_CLASS_UNSPECIFIED 0x00, 未指定
iOS 7.0 之前 优先级
DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
第二个参数:目前没有用,苹果预留的一个参数为了以后用。
*/
//为了适配iOS7 优先级直接写0,第二个参数没有用随便写
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//异步和同步就不写了,和前面的使用一模一样。
差不多就写到这,有不对的欢迎指正!