GCD
简介
- GCD全称是
Grand Central Dispatch
,由C语言
开发的。 - GCD是苹果公司为
多核
的并行运算提出的解决方案
。 - GCD会
自动利用更多的CPU内核
(比如双核、四核)。 - GCD会
自动管理线程的生命周期
(创建线程、调度任务、销毁线程)。因此在使用GCD的时候,只需要管理执行的任务,而不需要管理线程。
GCD核心
GCD的核心分为三步骤:
- 创建任务
- 创建队列
- 将任务添加到队列中,并指定执行任务的函数
根据核心三步骤,写一个简单的GCD代码
//1. 创建任务
dispatch_block_t block = ^{
NSLog(@"hello GCD");
};
//2. 创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL);
//3. 将任务添加到队列中,并指定执行方式,此处为异步执行
dispatch_async(queue, block);
//上面三个步骤的代码可以合并成以下形式
dispatch_async(dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL), {
NSLog(@"hello GCD");
});
注意:
- 任务需要使用
block
进行封装 - 任务的
block没有参数,也没有返回值
函数与队列
函数
在GCD中,执行任务的方式有两种:同步执行和异步执行。
-
同步执行
- 使用函数
dispatch_sync
必须等待当前语句执行完毕,才会执行下一条语句
-
不会开启线程
,即不具备开启新线程的能力 - 由于不会开启新线程,因此block任务是在当前线程中执行
- 使用函数
-
异步执行
- 使用函数
dispatch_async
不用等待当前语句执行完毕,就可以执行下一条语句
-
会开启线程
执行block任务,即具备开启新线程的能力(注意:不表示每一次异步执行都会开新线程)
- 使用函数
队列
多线程中所说的队列(Dispatch Queue)
是指执行任务的等待队列,即用来存放任务的队列
。队列是一种特殊的线性表,遵循先进先出(FIFO)
原则,即新任务总是被插入到队尾,而任务的读取从队首开始读取。每执行完一个任务,则任务队列中释放一个任务,如下图所示:
在GCD中,队列主要分为串行队列(Serial Dispatch Queue)
和并发队列(Concurrent Dispatch Queue)
两种,如下图所示
- 串行队列
-
每次
只有一个任务
被执行,等待上一个任务执行完毕再执行下一个 -
创建串行队列的方式如下:
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.HQ.Queue", NULL); dispatch_queue_t serialQueue2 = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL);
-
- 并发队列
-
一次可以并发
执行多个任务
,即开启多个线程,并同时执行任务 -
创建并发队列的方式如下:
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
-
在GCD中,提供了两种特殊的队列:主队列(Main Dispatch Queue)
和全局并发队列(Global Dispatch Queue)
。
-
主队列
-
主线程
上用于调度任务的串行队列
,依赖于主线程、主Runloop,在main函数调用之前自动创建,通常UI操作必须使用主队列
- 不会开启线程
- 如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度
- 使用
dispatch_get_main_queue()
获得主队列
-
-
全局并发队列
- GCD提供的
默认的并发队列
,使用多线程开发时,如果对队列没有特殊需求,在执行异步任务时,可以直接使用全局并发队列 - 使用
dispatch_get_global_queue
函数获取全局并发队列
//全局并发队列的获取方法 //参数1:队列优先级 //参数2:保留参数 dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); //优先级从高到低(对应的服务质量)依次为 - 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
- GCD提供的
函数与队列的不同组合
- 串行队列 + 同步函数:
任务是一个接着一个完成,不会开辟新的线程
。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL);
for(int i = 0; i<5; i++){
dispatch_sync(queue, ^{
NSLog(@"串行队列 + 同步执行 + %d", i);
NSLog(@"%@", [NSThread currentThread]);
});
}
NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
}
//执行结果如下
2021-04-02 14:30:09.694649+0800 001---函数与队列[79317:12540708] [任务开始] -- <NSThread: 0x600003bdc6c0>{number = 1, name = main}
2021-04-02 14:30:09.694932+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 0
2021-04-02 14:30:09.695138+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
2021-04-02 14:30:09.695302+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 1
2021-04-02 14:30:09.695468+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
2021-04-02 14:30:09.695619+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 2
2021-04-02 14:30:09.695794+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
2021-04-02 14:30:09.695957+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 3
2021-04-02 14:30:09.696411+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
2021-04-02 14:30:09.696766+0800 001---函数与队列[79317:12540708] 串行队列 + 同步执行 + 4
2021-04-02 14:30:09.697152+0800 001---函数与队列[79317:12540708] <NSThread: 0x600003bdc6c0>{number = 1, name = main}
2021-04-02 14:30:09.697933+0800 001---函数与队列[79317:12540708] [任务结束] -- <NSThread: 0x600003bdc6c0>{number = 1, name = main}
- 串行队列 + 异步函数:
任务是一个接着一个完成,会开辟新的线程
。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_SERIAL);
for(int i = 0; i<5; i++){
dispatch_async(queue, ^{
NSLog(@"串行队列 + 异步执行 + %d", i);
NSLog(@"%@", [NSThread currentThread]);
});
}
NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
}
//执行结果如下
2021-04-02 14:58:21.928131+0800 001---函数与队列[79640:12562323] [任务开始] -- <NSThread: 0x600001edc400>{number = 1, name = main}
2021-04-02 14:58:21.928465+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 0
2021-04-02 14:58:21.928495+0800 001---函数与队列[79640:12562323] [任务结束] -- <NSThread: 0x600001edc400>{number = 1, name = main}
2021-04-02 14:58:21.928672+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
2021-04-02 14:58:21.928861+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 1
2021-04-02 14:58:21.929018+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
2021-04-02 14:58:21.929183+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 2
2021-04-02 14:58:21.929364+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
2021-04-02 14:58:21.929862+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 3
2021-04-02 14:58:21.930451+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
2021-04-02 14:58:21.930978+0800 001---函数与队列[79640:12562423] 串行队列 + 异步执行 + 4
2021-04-02 14:58:21.931508+0800 001---函数与队列[79640:12562423] <NSThread: 0x600001eef5c0>{number = 6, name = (null)}
- 并发队列 + 同步函数:
任务是一个接着一个完成,不会开辟新的线程。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
for(int i = 0; i<5; i++){
dispatch_sync(queue, ^{
NSLog(@"并发队列 + 同步执行 + %d", i);
NSLog(@"%@", [NSThread currentThread]);
});
}
NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
}
//执行结果如下
2021-04-02 15:02:05.764874+0800 001---函数与队列[79685:12565854] [任务开始] -- <NSThread: 0x600000c3c5c0>{number = 1, name = main}
2021-04-02 15:02:05.765200+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 0
2021-04-02 15:02:05.765423+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
2021-04-02 15:02:05.765595+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 1
2021-04-02 15:02:05.765773+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
2021-04-02 15:02:05.765933+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 2
2021-04-02 15:02:05.766366+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
2021-04-02 15:02:05.766516+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 3
2021-04-02 15:02:05.766727+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
2021-04-02 15:02:05.767189+0800 001---函数与队列[79685:12565854] 并发队列 + 同步执行 + 4
2021-04-02 15:02:05.767685+0800 001---函数与队列[79685:12565854] <NSThread: 0x600000c3c5c0>{number = 1, name = main}
2021-04-02 15:02:05.768114+0800 001---函数与队列[79685:12565854] [任务结束] -- <NSThread: 0x600000c3c5c0>{number = 1, name = main}
- 并发队列 + 异步函数:
任务执行没有顺序,会开辟新的线程
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
for(int i = 0; i<5; i++){
dispatch_async(queue, ^{
NSLog(@"并发队列 + 异步执行 + %d", i);
NSLog(@"%@", [NSThread currentThread]);
});
}
NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
}
//执行结果如下
2021-04-02 15:04:09.229740+0800 001---函数与队列[79720:12568189] [任务开始] -- <NSThread: 0x600001254a80>{number = 1, name = main}
2021-04-02 15:04:09.230070+0800 001---函数与队列[79720:12568287] 并发队列 + 异步执行 + 1
2021-04-02 15:04:09.230078+0800 001---函数与队列[79720:12568293] 并发队列 + 异步执行 + 0
2021-04-02 15:04:09.230078+0800 001---函数与队列[79720:12568291] 并发队列 + 异步执行 + 2
2021-04-02 15:04:09.230091+0800 001---函数与队列[79720:12568189] [任务结束] -- <NSThread: 0x600001254a80>{number = 1, name = main}
2021-04-02 15:04:09.230113+0800 001---函数与队列[79720:12568286] 并发队列 + 异步执行 + 3
2021-04-02 15:04:09.230340+0800 001---函数与队列[79720:12568287] <NSThread: 0x600001214880>{number = 5, name = (null)}
2021-04-02 15:04:09.230347+0800 001---函数与队列[79720:12568293] <NSThread: 0x600001254640>{number = 6, name = (null)}
2021-04-02 15:04:09.230363+0800 001---函数与队列[79720:12568291] <NSThread: 0x6000012189c0>{number = 4, name = (null)}
2021-04-02 15:04:09.230494+0800 001---函数与队列[79720:12568286] <NSThread: 0x600001216000>{number = 7, name = (null)}
2021-04-02 15:04:09.230574+0800 001---函数与队列[79720:12568287] 并发队列 + 异步执行 + 4
2021-04-02 15:04:09.233691+0800 001---函数与队列[79720:12568287] <NSThread: 0x600001214880>{number = 5, name = (null)}
- 主队列 + 同步函数:
形成死锁
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
for(int i = 0; i<5; i++){
dispatch_sync(queue, ^{
NSLog(@"主队列 + 同步执行 + %d", i);
NSLog(@"%@", [NSThread currentThread]);
});
}
NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
}
总结:主队列+同步函数
时,会造成死锁。造成死锁原因如下:
- 主队列为串行队列,队列的特性为先进先出,执行完前面的任务才会开始执行后面的任务。
- 同步执行的特点是,必须等待当前语句执行完毕,才会执行下一条语句。
-
viewDidLoad
函数是主队列中正在执行的任务
因此,当新的任务block
添加至主队列中时,block任务在队列中位于viewDidLoad
函数后面,因此需要viewDidLoad
函数执行完成之后才将开始执行block任务。而同步执行的特点要求,必须执行完block的内容后再继续下一条语句。这样就造成了相互等待
的情况,即死锁。
- 主队列 + 异步函数:
按照主队列中任务顺序执行,不会开辟新线程。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
for(int i = 0; i<5; i++){
dispatch_async(queue, ^{
NSLog(@"主队列 + 异步执行 + %d", i);
NSLog(@"%@", [NSThread currentThread]);
});
}
NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
//执行结果如下
2021-04-02 15:08:38.209535+0800 001---函数与队列[79797:12573195] [任务开始] -- <NSThread: 0x600003610240>{number = 1, name = main}
2021-04-02 15:08:38.210107+0800 001---函数与队列[79797:12573195] [任务结束] -- <NSThread: 0x600003610240>{number = 1, name = main}
2021-04-02 15:08:38.261887+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 0
2021-04-02 15:08:38.263527+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
2021-04-02 15:08:38.264620+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 1
2021-04-02 15:08:38.267018+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
2021-04-02 15:08:38.268366+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 2
2021-04-02 15:08:38.270540+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
2021-04-02 15:08:38.271863+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 3
2021-04-02 15:08:38.274142+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
2021-04-02 15:08:38.275433+0800 001---函数与队列[79797:12573195] 主队列 + 异步执行 + 4
2021-04-02 15:08:38.279348+0800 001---函数与队列[79797:12573195] <NSThread: 0x600003610240>{number = 1, name = main}
- 全局队列 + 同步函数:
与并发队列+同步函数的情况相同,任务是一个接着一个完成,不会开辟新的线程。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for(int i = 0; i<5; i++){
dispatch_sync(queue, ^{
NSLog(@"全局队列 + 同步执行 + %d", i);
NSLog(@"%@", [NSThread currentThread]);
});
}
NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
}
//执行结果如下
2021-04-02 15:14:09.630073+0800 001---函数与队列[79868:12578208] [任务开始] -- <NSThread: 0x6000023e4440>{number = 1, name = main}
2021-04-02 15:14:09.630321+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 0
2021-04-02 15:14:09.630523+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
2021-04-02 15:14:09.630735+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 1
2021-04-02 15:14:09.630912+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
2021-04-02 15:14:09.631072+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 2
2021-04-02 15:14:09.631251+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
2021-04-02 15:14:09.631396+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 3
2021-04-02 15:14:09.631567+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
2021-04-02 15:14:09.631715+0800 001---函数与队列[79868:12578208] 全局队列 + 同步执行 + 4
2021-04-02 15:14:09.632059+0800 001---函数与队列[79868:12578208] <NSThread: 0x6000023e4440>{number = 1, name = main}
2021-04-02 15:14:09.632473+0800 001---函数与队列[79868:12578208] [任务结束] -- <NSThread: 0x6000023e4440>{number = 1, name = main}
- 全局队列 + 异步函数:
与并发队列+异步函数的情况相同,任务执行没有顺序,会开辟新的线程。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"[任务开始] -- %@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for(int i = 0; i<5; i++){
dispatch_async(queue, ^{
NSLog(@"全局队列 + 异步执行 + %d", i);
NSLog(@"%@", [NSThread currentThread]);
});
}
NSLog(@"[任务结束] -- %@", [NSThread currentThread]);
}
//执行结果如下
2021-04-02 15:17:26.686035+0800 001---函数与队列[79911:12581270] [任务开始] -- <NSThread: 0x6000016bc600>{number = 1, name = main}
2021-04-02 15:17:26.686456+0800 001---函数与队列[79911:12581270] [任务结束] -- <NSThread: 0x6000016bc600>{number = 1, name = main}
2021-04-02 15:17:26.686429+0800 001---函数与队列[79911:12581363] 全局队列 + 异步执行 + 1
2021-04-02 15:17:26.686475+0800 001---函数与队列[79911:12581362] 全局队列 + 异步执行 + 2
2021-04-02 15:17:26.686511+0800 001---函数与队列[79911:12581367] 全局队列 + 异步执行 + 0
2021-04-02 15:17:26.686623+0800 001---函数与队列[79911:12581360] 全局队列 + 异步执行 + 3
2021-04-02 15:17:26.686779+0800 001---函数与队列[79911:12581363] <NSThread: 0x600001683040>{number = 6, name = (null)}
2021-04-02 15:17:26.687020+0800 001---函数与队列[79911:12581362] <NSThread: 0x6000016a5bc0>{number = 4, name = (null)}
2021-04-02 15:17:26.687293+0800 001---函数与队列[79911:12581366] 全局队列 + 异步执行 + 4
2021-04-02 15:17:26.687970+0800 001---函数与队列[79911:12581367] <NSThread: 0x6000016f8a00>{number = 7, name = (null)}
2021-04-02 15:17:26.688492+0800 001---函数与队列[79911:12581360] <NSThread: 0x6000016a5e80>{number = 5, name = (null)}
2021-04-02 15:17:26.691704+0800 001---函数与队列[79911:12581366] <NSThread: 0x6000016b2840>{number = 3, name = (null)}
总结
函数\队列 | 串行队列 | 并发队列 | 主队列 | 全局队列 |
---|---|---|---|---|
同步执行 | 1. 顺序执行; 2.不会开辟新线程 |
1. 顺序执行; 2.不会开辟新线程 |
死锁 | 1. 顺序执行; 2.不会开辟新线程 |
异步执行 | 1. 顺序执行; 2.会开辟新线程 |
1. 乱序执行; 2.会开辟新线程 |
1. 顺序执行; 2.不会开辟新线程 |
1. 乱序执行; 2.会开辟新线程 |
【面试题 - 1】异步函数+并行队列
下面代码的输出顺序是什么?
- (void)interview01{
//并行队列
dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
// 耗时
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
//输出结果:
1 5 2 4 3
分析:
- 主线程的任务队列为:任务1、异步block1、任务5,其中异步block1会比较耗费性能,任务1和任务5的任务复杂度是相同的,所以任务1和任务5优先于异步block1执行
- 在异步block1中,任务队列为:任务2、异步block2、任务4,其中block2相对比较耗费性能,任务2和任务4是复杂度一样,所以任务2和任务4优先于block2执行
- 最后执行block2中的任务3
- 在极端情况下,可能出现 任务2先于任务1和任务5执行,原因是出现了当前主线程卡顿或者 延迟的情况
对代码进行修改,将并行队列
改成串行队列
,其结果是什么?
答:因为代码里面都是异步执行,所以结果仍然是“1 5 2 4 3”
继续修改代码,在任务5之前,休眠2s,即sleep(2),结果是什么?
答:结果可能变成“1 2 4 3 5”
【面试题 - 2】异步函数嵌套同步函数 + 并发队列
下面代码的输出顺序是什么?
- (void)interview02{
//并发队列
dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
//异步函数
dispatch_async(queue, ^{
NSLog(@"2");
//同步函数
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
//输出结果:
1 5 2 3 4
分析:
- 主线程的任务队列为:任务1、异步block1、任务5,其中异步block1会比较耗费性能,任务1和任务5的任务复杂度是相同的,所以任务1和任务5优先于异步block1执行
- 在异步block1中,任务队列为:任务2、同步block2、任务4,由于block2为同步执行,因此执行顺序为任务2 -> 同步block2 -> 任务4
【面试题 - 3】异步函数嵌套同步函数 + 串行队列(即同步队列)
下面代码的执行顺序是什么?
- (void)interview03{
// 同步队列
dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", NULL);
NSLog(@"1");
// 异步函数
dispatch_async(queue, ^{
NSLog(@"2");
// 同步函数
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
//输出结果:
1 5 2 死锁
分析:
- 主线程的任务队列为:任务1、异步block1、任务5,其中异步block1会比较耗费性能,任务1和任务5的任务复杂度是相同的,所以任务1和任务5优先于异步block1执行
- 在异步block1中,任务队列为:任务2、同步block2、任务4。由于block1是同步队列,同步队列的执行顺序为:任务2->同步block2->任务4。
- 在同步block2的串行队列中添加了任务3,同步队列的执行顺序为:任务2->同步block2->任务4->任务3。此时就出现了死锁。因为同步block2的执行依赖于任务3完成,而任务3的执行又依赖于任务4的执行完成,而任务4的完成依赖于同步block2的执行完成。
扩展:如果在上题的基础上去掉任务4,会发生什么?
答:仍然是发生死锁。因为此时任务3等待的是block2执行完毕,而block2等待任务3执行完成。
【面试题 - 4 - 新浪】 异步函数 + 同步函数 + 并发队列
下面代码的执行顺序是什么?
- (void)interview04{
//并发队列
dispatch_queue_t queue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{ // 耗时
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
// 同步
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"0");
dispatch_async(queue, ^{
NSLog(@"7");
});
dispatch_async(queue, ^{
NSLog(@"8");
});
dispatch_async(queue, ^{
NSLog(@"9");
});
}
//输出结果:
输出顺序为:(1 2 3 无序)0(7 8 9 无序)
分析:
- 任务1 和 任务2由于是异步函数+并发队列,会开启线程,所以没有固定顺序,
- 任务7、任务8、任务9也是异步函数+并发队列,会开启线程,所以没有固定顺序
- 任务3是同步函数+并发队列,同步函数会阻塞主线程,但是也只会阻塞0,所以,可以确定的是 0一定在3之后,在789之前
- 任务1、任务2、任务3复杂度相差不大,因此任务1、任务2、任务3执行无序。
下面代码中,队列的类型有几种?
//串行队列 - Serial Dispatch Queue
dispatch_queue_t serialQueue = dispatch_queue_create("com.HQ.Queue", NULL);
//并发队列 - Concurrent Dispatch Queue
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.HQ.Queue", DISPATCH_QUEUE_CONCURRENT);
//主队列 - Main Dispatch Queue
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//全局并发队列 - Global Dispatch Queue
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
答:队列类型共有两种:串行队列和并发队列
,主队列系统提供的串行队列,全局队列是系统提供的并发队列。
网友评论