面试题引发的思考:
Q: iOS的多线程方案有哪几种?你更倾向于哪一种?
- 常见的方案有四种:Pthread、NSThread、GCD、NSOperation;
- 比较常用的是GCD、NSOperation。
Q: GCD 的队列类型?
- 并发队列(Concurrent Dispatch Queue)
- 串行队列(Serial Dispatch Queue)
Q: 何时会产生死锁?
- 使用
dispatch_sync
函数 往 当前串行队列 中添加任务,会卡住当前的串行队列(产生死锁)。
Q: OperationQueue 和 GCD 的区别,以及各自的优势?
- 如下常见的多线程方案介绍
1. 常见的多线程方案
2. GCD的常用函数
(1) 2个执行任务的函数:
同步执行任务:
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
异步执行任务:
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
同步、异步区别:能否开启新的线程
- 同步:在 当前线程 中执行任务,不具备 开启新线程的能力;
- 异步:在 新的线程 中执行任务,具备 开启新线程的能力。
(2) 2个队列类型:
并发队列(Concurrent Dispatch Queue):
- 让多个任务并发(同时)执行(自动开启多个线程同时执行任务);
- 并发功能只有在异步(
dispatch_async
)函数下才有效。串行队列(Serial Dispatch Queue):
- 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)。
(3) 各种队列的执行效果
总结可知:
- 同步函数或者主队列:没有开启新线程、串行执行任务;
- 异步函数且非主队列:有开启新线程、执行方式取决与队列类型并发或串行
3. 面试题分析
(1) Q: 以下代码会不会产生死锁?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"执行任务 - 1");
// 队列的特点:FIFO,First In First Out,先进先出
dispatch_queue_t queue = dispatch_get_main_queue();
// dispatch_sync:立马在 当前线程 同步 执行任务
dispatch_sync(queue, ^{
// 当前线程,主队列 - 主线程执行
NSLog(@"执行任务 - 2");
});
NSLog(@"执行任务 - 3");
}
// 打印结果
Demo[1234:567890] 执行任务 - 1
(lldb)
根据程序做出分析图:
由分析图可知:
主线程执行任务1之后,需要同步(dispatch_sync
)执行任务2;
而dispatch_sync
要求立马在当前线程同步执行任务;
但是主队列中有viewDidLoad
需要执行完毕才能执行任务2;
因此会产生死锁。
(2) Q: 以下代码会不会产生死锁?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"执行任务 - 1");
dispatch_queue_t queue = dispatch_get_main_queue();
// dispatch_sync:不要求立马在 当前线程 同步 执行任务
dispatch_async(queue, ^{
NSLog(@"执行任务 - 2");
});
NSLog(@"执行任务 - 3");
}
// 打印结果
Demo[1234:567890] 执行任务 - 1
Demo[1234:567890] 执行任务 - 3
Demo[1234:567890] 执行任务 - 2
根据程序做出分析图:
由分析图可知:
主线程执行任务1之后,需要异步(dispatch_async
)执行任务2;
而dispatch_async
不要求立马在当前线程同步执行任务;
所以主线程接着执行任务3,最后异步执行任务2;
因此不会产生死锁。
(3) Q: 以下代码会不会产生死锁?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"执行任务 - 1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // block0
NSLog(@"执行任务 - 2");
dispatch_sync(queue, ^{ // block1
NSLog(@"执行任务 - 3");
});
NSLog(@"执行任务 - 4");
});
NSLog(@"执行任务 - 5");
}
// 打印结果
Demo[1234:567890] 执行任务 - 1
Demo[1234:567890] 执行任务 - 5
Demo[1234:567890] 执行任务 - 2
(lldb)
根据程序做出分析图:
由分析图可知:
子线程执行任务1之后,需要异步(dispatch_async
)执行任务2;而dispatch_sync
要求立马在当前线程同步执行任务;
所以主线程接着执行任务5;
接着需要在串行队列中同步(dispatch_sync
)执行任务3;
但是在串行队列中需要执行完任务4之后才能执行任务3;
因此会产生死锁。
(4) Q: 以下代码会不会产生死锁?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"执行任务 - 1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{ // block0
NSLog(@"执行任务 - 2");
dispatch_sync(queue2, ^{ // block1
NSLog(@"执行任务 - 3");
});
NSLog(@"执行任务 - 4");
});
NSLog(@"执行任务 - 5");
}
// 打印结果
Demo[1234:567890] 执行任务 - 1
Demo[1234:567890] 执行任务 - 5
Demo[1234:567890] 执行任务 - 2
Demo[1234:567890] 执行任务 - 3
Demo[1234:567890] 执行任务 - 4
根据程序做出分析图:
由分析图可知:
子线程执行任务1之后,需要异步(dispatch_async
)执行任务2;
所以先执行主线程的任务5,然后执行任务2;
接着需要在并发队列中同步(dispatch_sync
)执行任务3;
然后执行串行队列中的任务4;
因此不会产生死锁。
(5) Q: 以下代码会不会产生死锁?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"执行任务 - 1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // block0
NSLog(@"执行任务 - 2");
dispatch_sync(queue2, ^{ // block1
NSLog(@"执行任务 - 3");
});
NSLog(@"执行任务 - 4");
});
NSLog(@"执行任务 - 5");
}
// 打印结果
Demo[1234:567890] 执行任务 - 1
Demo[1234:567890] 执行任务 - 5
Demo[1234:567890] 执行任务 - 2
Demo[1234:567890] 执行任务 - 3
Demo[1234:567890] 执行任务 - 4
根据程序做出分析图:
由分析图可知:
子线程执行任务1之后,需要异步(dispatch_async
)执行任务2;
所以先执行主线程的任务5,然后执行任务2;
接着需要在串行队列中同步(dispatch_sync
)执行任务3;
然后执行串行队列中的任务4;
因此不会产生死锁。
6> Q: 以下代码会不会产生死锁?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"执行任务 - 1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{ // block0
NSLog(@"执行任务 - 2");
dispatch_sync(queue, ^{ // block1
NSLog(@"执行任务 - 3");
});
NSLog(@"执行任务 - 4");
});
NSLog(@"执行任务 - 5");
}
// 打印结果
Demo[1234:567890] 执行任务 - 1
Demo[1234:567890] 执行任务 - 5
Demo[1234:567890] 执行任务 - 2
Demo[1234:567890] 执行任务 - 3
Demo[1234:567890] 执行任务 - 4
根据程序做出分析图:
由分析图可知:
子线程执行任务1之后,需要异步(dispatch_async
)执行任务2;
所以先执行主线程的任务5,然后执行任务2;
接着需要在并发队列中同步(dispatch_sync
)执行任务3;
然后执行并发队列中的任务4;
因此不会产生死锁。
(6) 总结可知:
使用
dispatch_sync
函数 往 当前串行队列 中添加任务,会卡住当前的串行队列(产生死锁)。