这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战
1. GCD简介
- GCD全称是
Grand Central Dispatch
- 纯C语言,提供了非常多强大的函数
- GCD作用,将任务添加到队列,并指定任务执行的函数
1.1 GCD的优势
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核,四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁任务)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
1.2 GCD基础
GCD的基本用法
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
});
还原最原始的写法,可拆分成任务,队列和函数
- (void)syncTest{
// 任务
dispatch_block_t block = ^{
NSLog(@"hello GCD");
};
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("com.lg.cn", NULL);
// 函数
dispatch_async(queue, block); }
- 使用
dispatch_block_t
创建任务- 任务使用
block
封装 - 任务的
block
没有参数,也没有返回值
- 任务使用
- 使用
dispatch_queue_t
创建队列 - 将任务添加到队列,并指定执行任务的函数
dispatch_async
2. 函数
在GCD中执行任务的方式有两种,同步执行和异步执行
dispatch_sync
:同步函数dispatch_async
:异步函数
2.1 dispatch_sync
同步函数dispatch_sync的特性
- 必须等待当前语句执行完毕,才能执行下一条语句
- 不会开启线程,即不具备开启新线程的能力
- 在当前线程中执行block任务
2.2 dispatch_async
异步函数dispatch_async的特性
- 不同等待当前语句执行完毕,就可以执行下一条语句
- 会开启线程执行block任务,即具备开启新线程的能力(但并不一定开启新线程,这个与任务所指定的队列类型有关)
- 异步是多线程的代名词
2.3 二者的区别
- 是否等待队列的任务执行完毕
- 是否具备开启新线程的能力
3. 队列
队列分为串行队列和并发队列,用来存放任务。队列是一种数据结构,属于特殊的线性表,遵循先进先出(FIFC)原则。新任务被插入到队尾,而任务的读取从队首开时。每读取一个任务,则队伍中释放一个任务。
在GCD中,还提供了两个特殊的队列,分别是主队列
和全局并发队列
。主队列属于串行队列
,而全局并发队列属于并发队列
。
队列和线程并没有关系,队列负责任务的调度,任务的执行依赖于线程,优先调度的任务不一定优先执行。
3.1 串行队列
串行队列:Serial Dispatch Queue
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.lg.serial", NULL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.lg.serial", DISPATCH_QUEUE_SERIAL);
- 每次只有一个任务被执行,等待上一个任务执行完毕再执行下一个
- 只开启一个线程,同一时刻只调度一个任务执行
- 使用
DISPATCH_QUEUE_SERIAL
创建串行队列 DISPATCH_QUEUE_SERIAL
也可传入NULL,默认创建为串行队列
3.2 并发队列
并发队列:Concurrent Dispatch Queue
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.lg.concurrent", DISPATCH_QUEUE_CONCURRENT);
- 一次可以并发执行多个任务
- 开启多线程,同一时刻可以调度多个任务执行
- 使用
DISPATCH_QUEUE_CONCURRENT
创建并发队列 - 并发队列的并发功能只有在异步函数下才有效
3.3 主队列
主队列:Main Dispatch Queue
dispatch_queue_t mainQueue = dispatch_get_main_queue();
- 主队列:GCD提供的特殊的串行队列
- 专门用来在主线程上调度任务的串行队列,依赖于主线程、主Runloop,在main函数调用之前自动创建
- 不会开启线程
- 如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度
- 使用
dispatch_get_main_queue
获得主队列 - 通常在返回主线程更新UI时使用
3.4 全局并发队列
全局并发队列:Global Dispatch Queue
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
- GCD提供的默认的并发队列
- 为了方便开发者使用,苹果提供了全局队列
- 在使用所线程开发时,如果对队列没有特殊需求,在执行一步任务时,可以直接使用全局队列
- 使用
dispatch_get_global_queue
获取全局并发队列,最简单的是dispatch_get_global_queue(0,0)
- 参数1表示队列优先级,默认优先级为
DISPATCH_QUEUE_PRIORITY_DEFAULT = 0
,被服务质量quality of service
取代 - 参数2为标记,是为了未来使用保留的。所以这个参数应该永远指定为0 优先级从高到低,对应服务质量
- 参数1表示队列优先级,默认优先级为
DISPATCH_QUEUE_PRIORITY_HIGH
:QOS_CLASS_USER_INITIATED
DISPATCH_QUEUE_PRIORITY_DEFAULT
:QOS_CLASS_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
:QOS_CLASS_UTILITY
DISPATCH_QUEUE_PRIORITY_BACKGROUND
:QOS_CLASS_BACKGROUND
日常开发中,主队列+全局并发队列的使用:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//执行耗时操作
dispatch_async(dispatch_get_main_queue(), ^{
//回到主线程进行UI操作
});
});
4.队列与函数的搭配使用
队列/函数 | 同步函数 | 异步函数 |
---|---|---|
串行队列 | 不会开启线程,在当前线程执行任务串行执行,一个接一个 | 开启新线程任务串行执行,一个接一个 |
并发队列 | 不会开启线程,在当前线程执行任务串行执行,一个接一个 | 开启新线程任务异步执行,没有顺序,和CPU 调度有关 |
主队列 | 死锁 | 不会开启线程,在当前线程执行任务串行执行,一个接一个 |
全局队列 | 不会开启线程,在当前线程执行任务串行执行,一个接一个 | 开启新线程任务异步执行,没有顺序,和CPU 调度有关 |
4.1 串行队列
4.1.1 同步队列
// 同步串行队列
- (void)serialSyncTest{
dispatch_queue_t queue = dispatch_queue_create("com.lg.cn", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i<5; i++) {
dispatch_sync(queue, ^{
NSLog(@"%d:%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
-------------------------
//输出以下内容:
0:<NSThread: 0x2807e83c0>{number = 1, name = main}
1:<NSThread: 0x2807e83c0>{number = 1, name = main}
2:<NSThread: 0x2807e83c0>{number = 1, name = main}
3:<NSThread: 0x2807e83c0>{number = 1, name = main}
4:<NSThread: 0x2807e83c0>{number = 1, name = main}
hello queue
- 不会开启线程,在当前线程执行
- 任务串行执行,一个接一个
4.1.2 异步函数
// 异步串行队列
- (void)serialAsyncTest{
dispatch_queue_t queue = dispatch_queue_create("com.lg.cn", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i<5; i++) {
dispatch_async(queue, ^{
NSLog(@"%d:%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
-------------------------
//输出以下内容:
hello queue
0:<NSThread: 0x282ba5740>{number = 5, name = (null)}
1:<NSThread: 0x282ba5740>{number = 5, name = (null)}
2:<NSThread: 0x282ba5740>{number = 5, name = (null)}
3:<NSThread: 0x282ba5740>{number = 5, name = (null)}
4:<NSThread: 0x282ba5740>{number = 5, name = (null)}
- 开启新线程
- 任务串行执行,一个接一个
4.2 并发队列
4.2.1 同步函数
// 同步并发队列
- (void)concurrentSyncTest{
dispatch_queue_t queue = dispatch_queue_create("com.lg.cn", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<5; i++) {
dispatch_sync(queue, ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
-------------------------
//输出以下内容:
0:<NSThread: 0x2807e83c0>{number = 1, name = main}
1:<NSThread: 0x2807e83c0>{number = 1, name = main}
2:<NSThread: 0x2807e83c0>{number = 1, name = main}
3:<NSThread: 0x2807e83c0>{number = 1, name = main}
4:<NSThread: 0x2807e83c0>{number = 1, name = main}
hello queue
- 不会开启线程,在当前线程执行
- 任务串行执行,一个接一个
4.2.2 异步函数
// 异步并发队列
- (void)concurrentAsyncTest{
dispatch_queue_t queue = dispatch_queue_create("com.lg.cn", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<5; i++) {
dispatch_async(queue, ^{
NSLog(@"%d:%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
-------------------------
//输出以下内容:
hello queue
1:<NSThread: 0x2801ef300>{number = 10, name = (null)}
0:<NSThread: 0x2801ee300>{number = 8, name = (null)}
2:<NSThread: 0x2801e9b00>{number = 9, name = (null)}
3:<NSThread: 0x2801edf00>{number = 6, name = (null)}
4:<NSThread: 0x2801ee7c0>{number = 4, name = (null)}
- 开启新线程
- 任务异步执行,没有顺序,和CPU调度有关
4.3 主队列
4.3.1 同步函数
// 同步主队列
- (void)mainSyncTest{
dispatch_queue_t queue = dispatch_get_main_queue();
for (int i = 0; i<5; i++) {
dispatch_sync(queue, ^{
NSLog(@"%d:%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
-------------------------
//程序崩溃:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1010b4be0)
- 主队列的作用,专门用来在主线程上调度任务的串行队列
- 主队列中增加同步函数,导致主线程需要等待同步函数完成后再执行
- 由于主队列是特殊的串行队列,同步函数需要等待主线程完成后再执行
- 所以,两个任务相互等待,产生死锁
4.3.2 异步函数
// 异步主队列
- (void)mainAsyncTest{
dispatch_queue_t queue = dispatch_get_main_queue();
for (int i = 0; i<5; i++) {
dispatch_async(queue, ^{
NSLog(@"%d:%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
-------------------------
//输出以下内容:
hello queue
0:<NSThread: 0x280ed0200>{number = 1, name = main}
1:<NSThread: 0x280ed0200>{number = 1, name = main}
2:<NSThread: 0x280ed0200>{number = 1, name = main}
3:<NSThread: 0x280ed0200>{number = 1, name = main}
4:<NSThread: 0x280ed0200>{number = 1, name = main}
- 不会开启线程,在当前线程执行
- 任务串行执行,一个接一个
4.4 全局队列
4.4.1 同步函数
// 同步全局队列
- (void)globalSyncTest{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<5; i++) {
dispatch_sync(queue, ^{
NSLog(@"%d:%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
-------------------------
//输出以下内容:
0:<NSThread: 0x283a948c0>{number = 1, name = main}
1:<NSThread: 0x283a948c0>{number = 1, name = main}
2:<NSThread: 0x283a948c0>{number = 1, name = main}
3:<NSThread: 0x283a948c0>{number = 1, name = main}
4:<NSThread: 0x283a948c0>{number = 1, name = main}
hello queue
- 不会开启线程,在当前线程执行
- 任务串行执行,一个接一个
4.4.2 异步函数
// 异步全局队列
- (void)globalAsyncTest{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<5; i++) {
dispatch_async(queue, ^{
NSLog(@"%d:%@",i,[NSThread currentThread]);
});
}
NSLog(@"hello queue");
}
-------------------------
//输出以下内容:
hello queue
0:<NSThread: 0x2813099c0>{number = 9, name = (null)}
1:<NSThread: 0x281372b80>{number = 4, name = (null)}
2:<NSThread: 0x281371e40>{number = 10, name = (null)}
3:<NSThread: 0x281372080>{number = 6, name = (null)}
4:<NSThread: 0x281372c00>{number = 3, name = (null)}
- 开启新线程
- 任务异步执行,没有顺序,和CPU调度有关
4.5 线程死锁
所谓线程死锁,是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行
- 函数调用栈中,
_dispatch_sync_f_slow
即为死锁的异常