GCD,全称Grand Central Dispatch,可译为“牛逼的中枢调度器”, Apple 开发的一个多核编程的解决方法,纯C语言,提供了非常多强大的函数,在 iOS 4 及以上版本使用。
GCD优势
- GCD是苹果公司为多核的并行运算提出的解决方案。
- GCD 会自动利用更多的 CPU 内核(比如双核、四核)。
- GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)。
- 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码。
任务与队列
任务
就是要执行的操作,换句话说就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。
同步执行(sync):
- 添加任务到指定的队列中,在队列中之前的任务执行完之前,会一直等待,直到队列中之前的任务执行完再继续执行。
- 只能在当前线程中执行任务,不具备开启新线程的能力。
异步执行(async):
- 添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
- 可以在新的线程中执行任务,具备开启新线程的能力。
注意事项:
异步执行(async)虽然具有开启新线程的能力,但是并不一定开启新线程。这跟任务所指定的队列类型有关(下面会讲)。
队列
这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在 GCD 中有两种队列:串行队列和并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。
串行队列(Serial Dispatch Queue):
- 每次只有一个任务被执行。让任务一个接着一个地执行。
并发队列(Concurrent Dispatch Queue):
- 可以让多个任务并发(同时)执行。
GCD提供了两个特殊队列,一个是串行队列:主队列(Main Dispatch Queue),另一个是并发队列:全局并发队列(Global Dispatch Queue)
队列与任务的创建方法
- 队列的创建方法
/**
串行队列的创建方法
第一个参数const char *label : C语言字符串,用来标识
第二个参数dispatch_queue_attr_t attr : 队列的类型
并发队列:DISPATCH_QUEUE_CONCURRENT
串行队列:DISPATCH_QUEUE_SERIAL 或者 NULL
*/
dispatch_queue_t serialQueue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
// 主队列的获取方法
dispatch_queue_t mainQueue = dispatch_get_main_queue();
/**
全局并发队列的获取方法
第一个参数:优先级 也可直接填后面的数字
#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 // 后台
第二个参数: 预留参数,暂时没用,用0即可
*/
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- 任务的创建方法
// 同步任务创建方法
dispatch_sync(queue, ^{
// 这里放同步任务代码
});
// 异步任务创建方法
dispatch_async(queue, ^{
// 这里放异步任务代码
});
任务与队列组合
任务与队列的组合为:
- 同步任务 + 串行队列
- 同步任务 + 并发队列
- 同步任务 + 主队列
- 异步任务 + 串行队列
- 异步任务 + 并发队列
- 异步任务 + 主队列
同步任务 + 全局并发队列异步任务 + 全局并发队列
基于 全局并发队列 与 普通并发队列 在与任务组合过程中,并无差别,在下文代码展示中,不考虑全局并发队列的组合。
组合原则:任务>队列(主队列除外)
区别 | 串行队列 | 并发队列 | 主队列 |
---|---|---|---|
同步(sync) | 没有开启新线程,串行执行任务 | 没有开启新线程,串行执行任务 | 死锁卡住不执行 |
异步(async) | 有开启新线程(1条),串行执行任务 | 有开启新线程,并发执行任务 | 没有开启新线程,串行执行任务 |
- 同步任务 + 串行队列
/// 同步任务 + 串行队列
- (void)syncSerial {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"syncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"syncSerial---end");
/* 结果 - 没有开启新线程,串行执行任务
2019-04-22 01:24:07.414305+0800 GCD[1843:3137709] currentThread---<NSThread: 0x600000a6d380>{number = 1, name = main}
2019-04-22 01:24:07.414408+0800 GCD[1843:3137709] syncSerial---begin
2019-04-22 01:24:09.414778+0800 GCD[1843:3137709] 1---<NSThread: 0x600000a6d380>{number = 1, name = main}
2019-04-22 01:24:11.416190+0800 GCD[1843:3137709] 1---<NSThread: 0x600000a6d380>{number = 1, name = main}
2019-04-22 01:24:13.416811+0800 GCD[1843:3137709] 2---<NSThread: 0x600000a6d380>{number = 1, name = main}
2019-04-22 01:24:15.418364+0800 GCD[1843:3137709] 2---<NSThread: 0x600000a6d380>{number = 1, name = main}
2019-04-22 01:24:17.419142+0800 GCD[1843:3137709] 3---<NSThread: 0x600000a6d380>{number = 1, name = main}
2019-04-22 01:24:19.420796+0800 GCD[1843:3137709] 3---<NSThread: 0x600000a6d380>{number = 1, name = main}
2019-04-22 01:24:19.421075+0800 GCD[1843:3137709] syncSerial---end
*/
}
- 同步任务 + 并发队列
/// 同步任务 + 并发队列
- (void)syncConcurrent {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"syncConcurrent---end");
/* 结果 - 没有开启新线程,串行执行任务
2019-04-22 01:28:24.544927+0800 GCD[1905:3172908] currentThread---<NSThread: 0x60000136e980>{number = 1, name = main}
2019-04-22 01:28:24.545157+0800 GCD[1905:3172908] syncConcurrent---begin
2019-04-22 01:28:26.545693+0800 GCD[1905:3172908] 1---<NSThread: 0x60000136e980>{number = 1, name = main}
2019-04-22 01:28:28.547100+0800 GCD[1905:3172908] 1---<NSThread: 0x60000136e980>{number = 1, name = main}
2019-04-22 01:28:30.548452+0800 GCD[1905:3172908] 2---<NSThread: 0x60000136e980>{number = 1, name = main}
2019-04-22 01:28:32.549368+0800 GCD[1905:3172908] 2---<NSThread: 0x60000136e980>{number = 1, name = main}
2019-04-22 01:28:34.550001+0800 GCD[1905:3172908] 3---<NSThread: 0x60000136e980>{number = 1, name = main}
2019-04-22 01:28:36.550777+0800 GCD[1905:3172908] 3---<NSThread: 0x60000136e980>{number = 1, name = main}
2019-04-22 01:28:36.551122+0800 GCD[1905:3172908] syncConcurrent---end
*/
}
- 同步任务 + 主队列
/// 同步任务 + 主队列
- (void)syncMain {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"syncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"syncMain---end");
/* 结果 - crash在 dispatch_sync(queue, ^{ 这一行
2019-04-22 01:35:18.443455+0800 GCD[1970:3228432] currentThread---<NSThread: 0x600003f39fc0>{number = 1, name = main}
2019-04-22 01:35:18.443574+0800 GCD[1970:3228432] syncMain---begin
(lldb)
*/
}
- 异步任务 + 串行队列
/// 异步任务 + 串行队列
- (void)asyncSerial {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"asyncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"asyncSerial---end");
/* 结果 - 有开启新线程(1条),串行执行任务
2019-04-22 01:38:08.254765+0800 GCD[2008:3251926] currentThread---<NSThread: 0x6000019cbd00>{number = 1, name = main}
2019-04-22 01:38:08.254873+0800 GCD[2008:3251926] asyncSerial---begin
2019-04-22 01:38:08.254960+0800 GCD[2008:3251926] asyncSerial---end
2019-04-22 01:38:10.255499+0800 GCD[2008:3252126] 1---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
2019-04-22 01:38:12.259319+0800 GCD[2008:3252126] 1---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
2019-04-22 01:38:14.261561+0800 GCD[2008:3252126] 2---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
2019-04-22 01:38:16.265208+0800 GCD[2008:3252126] 2---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
2019-04-22 01:38:18.265802+0800 GCD[2008:3252126] 3---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
2019-04-22 01:38:20.266188+0800 GCD[2008:3252126] 3---<NSThread: 0x6000019a1600>{number = 3, name = (null)}
*/
}
- 异步任务 + 并发队列
/// 异步任务 + 并发队列
- (void)asyncConcurrent {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"asyncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"asyncConcurrent---end");
/* 结果 - 有开启新线程,并发执行任务
2019-04-22 01:40:21.313343+0800 GCD[2042:3270436] currentThread---<NSThread: 0x600001ac8040>{number = 1, name = main}
2019-04-22 01:40:21.313457+0800 GCD[2042:3270436] asyncConcurrent---begin
2019-04-22 01:40:21.313545+0800 GCD[2042:3270436] asyncConcurrent---end
2019-04-22 01:40:23.318284+0800 GCD[2042:3270652] 3---<NSThread: 0x600001a9f780>{number = 4, name = (null)}
2019-04-22 01:40:23.318318+0800 GCD[2042:3270655] 1---<NSThread: 0x600001a9b680>{number = 3, name = (null)}
2019-04-22 01:40:23.318401+0800 GCD[2042:3270656] 2---<NSThread: 0x600001a8a480>{number = 5, name = (null)}
2019-04-22 01:40:25.319365+0800 GCD[2042:3270652] 3---<NSThread: 0x600001a9f780>{number = 4, name = (null)}
2019-04-22 01:40:25.319386+0800 GCD[2042:3270655] 1---<NSThread: 0x600001a9b680>{number = 3, name = (null)}
2019-04-22 01:40:25.319441+0800 GCD[2042:3270656] 2---<NSThread: 0x600001a8a480>{number = 5, name = (null)}
*/
}
- 异步任务 + 主队列
/// 异步任务 + 主队列
- (void)asyncMain {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"asyncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"asyncMain---end");
/* 结果 - 没有开启新线程,串行执行任务
2019-04-22 01:42:23.007175+0800 GCD[2075:3287465] currentThread---<NSThread: 0x6000013f8d00>{number = 1, name = main}
2019-04-22 01:42:23.007279+0800 GCD[2075:3287465] asyncMain---begin
2019-04-22 01:42:23.007345+0800 GCD[2075:3287465] asyncMain---end
2019-04-22 01:42:25.011764+0800 GCD[2075:3287465] 1---<NSThread: 0x6000013f8d00>{number = 1, name = main}
2019-04-22 01:42:27.013156+0800 GCD[2075:3287465] 1---<NSThread: 0x6000013f8d00>{number = 1, name = main}
2019-04-22 01:42:29.013723+0800 GCD[2075:3287465] 2---<NSThread: 0x6000013f8d00>{number = 1, name = main}
2019-04-22 01:42:31.015146+0800 GCD[2075:3287465] 2---<NSThread: 0x6000013f8d00>{number = 1, name = main}
2019-04-22 01:42:33.016033+0800 GCD[2075:3287465] 3---<NSThread: 0x6000013f8d00>{number = 1, name = main}
2019-04-22 01:42:35.017703+0800 GCD[2075:3287465] 3---<NSThread: 0x6000013f8d00>{number = 1, name = main}
*/
}
总结
-
任务
>队列
(主队列除外)。 - 开不开线程,取决于
任务
,同步任务
不开新线程,异步任务
开新线程。 - 开几条线程取决于
队列
,串行队列
开一条线程,并行队列
在执行多个异步任务时会开辟多条线程。 -
同步任务+主队列
会crash,异步任务+主队列
不会开线程,在主线程执行任务
网友评论