在上一篇文章中,我们主要分析了同步、异步,并发队列和串行队列。相信看过的朋友应该有初步的认识,但是总觉得朦朦胧胧,今天我们通过几个例子,来进一步认识队列在开发过程中怎么使用。
注:本文所举的几个例子,都是在主线程中运行。
demo1
下面代码是否可以正常运行,如果可用正常运行,会输出什么结果:
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2");
});
NSLog(@"执行任务3");
运行以上代码,报错,错误原因:死锁。
下面我们来分析一下是什么原因导致的。
1.这段代码里有三个任务,分别是任务1,任务2,任务3。任务1和任务3运行在主线程,任务2运行在主队列;
2.主线程也是在主队列中的一个任务,可以认为先从主队列中取出,开始执行任务1;
3.代码运行到开始执行任务2,由于是sync,会阻塞当前任务,即任务3被阻断,等待任务2完成后,继续执行;
4.任务2在主队列中,按照队列的规则,FIFO,需要等待之前的任务(A)执行完成后,任务2方可出队执行;
5.任务A执行完成,其实就是等待任务3执行完成;
注意:此时死锁已经产生,任务2阻断了任务3的继续执行,想要执行任务2,需要等待主队列之前的任务完成出队,之前的任务又在等待任务3执行。循环等待,故而产生死锁。
答案:这段代码不能正常运行,会产生死锁,原因见以上5条。
demo2
问题:以下代码是在主线程执行的,会不会产生死锁?
// 问题:以下代码是在主线程执行的,会不会产生死锁?不会!
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"执行任务2");
});
NSLog(@"执行任务3");
// dispatch_async不要求立马在当前线程同步执行任务
运行代码,可以正常运行,结果如下:
2020-01-20 17:10:38.830762+0800 Interview04-gcd[8327:1668596] 执行任务1
2020-01-20 17:10:38.830950+0800 Interview04-gcd[8327:1668596] 执行任务3
2020-01-20 17:10:38.852216+0800 Interview04-gcd[8327:1668596] 执行任务2
分析:
对比第一道题目,这里不同的地方是任务2是在异步主队列中执行。
也就是说任务2不会阻塞任务3 的执行,任务3执行完成后,从主队列中取出任务2,执行。
所以我们看到的执行顺序是任务1,任务3,任务2。
和执行结果一致。
答案:不会产生死锁
demo3
问题:以下代码是在主线程执行的,会不会产生死锁?
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // 0
NSLog(@"执行任务2");
dispatch_sync(queue, ^{ // 1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
运行结果:崩溃,依旧产生死锁。
分析:其实这个和第一道题目是同样的道理,任务2、3、4在同一个队列里。任务3阻塞了任务4,同时在等待任务4的完成,造成死锁。
第一道题目是在主队列中,其实主队列也是一个串行队列。
答案:会产生死锁。
demo4
问题:以下代码是在主线程执行的,会不会产生死锁?
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
// dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // 0
NSLog(@"执行任务2");
dispatch_sync(queue2, ^{ // 1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
运行不会产生死锁,结果如下:
2020-01-20 17:39:39.733786+0800 Interview04-gcd[8844:1771353] 执行任务1
2020-01-20 17:39:39.733951+0800 Interview04-gcd[8844:1771353] 执行任务5
2020-01-20 17:39:39.733969+0800 Interview04-gcd[8844:1771764] 执行任务2
2020-01-20 17:39:39.734084+0800 Interview04-gcd[8844:1771764] 执行任务3
2020-01-20 17:39:39.734193+0800 Interview04-gcd[8844:1771764] 执行任务4
分析:与题目3相比,虽然任务3阻塞了任务4,但是任务3和任务4是在不同的队列中,任务4会等待任务3执行完成后,继续执行。最终的执行顺序就是 任务1,任务5,任务2,任务3,任务4.
答案:不会产生死锁。
demo5
问题:以下代码是在主线程执行的,会不会产生死锁?
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{ // 0
NSLog(@"执行任务2");
dispatch_sync(queue, ^{ // 1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
运行不会产生死锁,结果如下:
2020-01-20 17:43:45.141209+0800 Interview04-gcd[8966:1786471] 执行任务1
2020-01-20 17:43:45.141392+0800 Interview04-gcd[8966:1786471] 执行任务5
2020-01-20 17:43:45.141403+0800 Interview04-gcd[8966:1786985] 执行任务2
2020-01-20 17:43:45.141545+0800 Interview04-gcd[8966:1786985] 执行任务3
2020-01-20 17:43:45.141648+0800 Interview04-gcd[8966:1786985] 执行任务4
分析:任务2、3、4都在同一个并行队列中,任务3虽然阻塞了任务4,但是由于是在并行队列中,并不影响任务3的执行,任务3 执行完成后,继续执行任务4。最终的执行顺序就是 任务1,任务5,任务2,任务3,任务4。
答案:不会产生死锁。
总结
1.在同步(dispatch_sync())中,往当前串行队列添加任务就会产生死锁;
2.其他情况不会产生死锁。