欢迎私信探讨。
-
GCD中队列分为两种
-
串行队列:
串行队列的特点是队列中的任务会一个一个的执行,前一个任务不执行完成下一个任务就不会执行。
创建方式:
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
-
并发队列:
并发队列的特点是不管你多少的任务都会同时执行,任务一般并发队列和异步方式配对使用。
创建方式:
dispatch_queue_t queue = dispatch_queue_create("并发", DISPATCH_QUEUE_CONCURRENT);
-
-
线程执行方式也分为两种
-
同步执行:
同步执行的特点是,不具备开启新线程能力,且会阻塞当前线程。
调用方式:
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
-
异步执行:
异步执行的特点是,具备开启新线程能力,不会阻塞当前线程;但是只是说它具备这个能力,并不是一定会开启子线程,因为在主队列中异步执行依然会是在主线程中执行。
调用方式:
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
-
接下来将通过串行并发同步异步穿插介绍其中原理。无特殊说明默认在主线程中
serial sync 执行
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
sleep(2);
NSLog(@"结束----%@",[NSThread currentThread]);
});
NSLog(@"0000");
输出:
2019-06-19 17:49:34.547 fasfsdfs[21016:325617] 开始----<NSThread: 0x6000018ac140>{number = 1, name = main}
2019-06-19 17:49:36.548 fasfsdfs[21016:325617] 结束----<NSThread: 0x6000018ac140>{number = 1, name = main}
2019-06-19 17:49:36.548 fasfsdfs[21016:325617] 0000
分析
- 打印第一句:因为是同步,所以会阻塞主线程去执行 queue 队列中的任务,输出第一句,由打印
0000
的时间和打印开始----
的时间对比可得。 - 接下来的输出都是按部就班的打印了,没有什么好深入的。
serial sync 任务中嵌套 sync 任务
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"111----%@",[NSThread currentThread]);
});
NSLog(@"结束----%@",[NSThread currentThread]);
});
NSLog(@"0000");
输出:
2019-06-19 18:11:26.510 fasfsdfs[21508:339513] 开始----<NSThread: 0x600003ce1540>{number = 1, name = main}
分析:
- 输出
开始---
是正确的,上面分析过 - 如果你对死锁有了解或者跑了代码就会发现,程序会
crash
,因为造成了线程死锁。这一点需要重点讲解一下。
这里发生死锁的原因是:在串行队列中同步任务中嵌套了一个新的同步任务。
根据串行队列中任务是一个一个的执行,同步执行是会阻塞线程的,当执行到第二个dispatch_sync
时,它阻塞线程去等待第一个dispatch_sync
里面的任务先去执行完,而第二个同步任务却是在第一个同步任务里面,只有第二个同步执行完了才会继续执行接下来的代码,这样你看着我,我看着你,双方都卡住了,就造成了死锁。
serial sync 任务中嵌套 async 任务
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"111----%@",[NSThread currentThread]);
});
sleep(2);
NSLog(@"结束----%@",[NSThread currentThread]);
});
输出:
2019-06-19 18:37:54.614 fasfsdfs[21713:343984] 开始----<NSThread: 0x6000024b94c0>{number = 1, name = main}
2019-06-19 18:37:56.615 fasfsdfs[21713:343984] 结束----<NSThread: 0x6000024b94c0>{number = 1, name = main}
2019-06-19 18:37:56.615 fasfsdfs[21713:355085] 111----<NSThread: 0x600002426640>{number = 14, name = (null)}
分析:
- 串行任务依次执行,当执行到 async 时需要等当前的任务执行完成才回去执行里面的任务,因为是异步的所以不会阻塞线程,且不是在主队列中所以会开起子线程执行,综上所述不会造成死锁。
serial async 任务中嵌套 sync
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"111----%@",[NSThread currentThread]);
});
NSLog(@"结束----%@",[NSThread currentThread]);
});
NSLog(@"0000");
输出:
2019-06-20 16:44:02.738 fasfsdfs[34323:745553] 开始----<NSThread: 0x600003558e80>{number = 5, name = (null)}
分析:
- 虽然是在异步调用的子线程中执行
sync
任务,但是依然会在调用sync
时crash
。原理同上,串行队列任务一个个执行,上一个任务不执行完成,下一个任务就不会执行,而sync
属于任务嵌套中的任务。
serial async 任务中嵌套 async
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"111----%@",[NSThread currentThread]);
});
sleep(2);
NSLog(@"结束----%@",[NSThread currentThread]);
});
输出:
2019-06-20 17:21:28.635 fasfsdfs[35506:772778] 开始----<NSThread: 0x6000001dde40>{number = 7, name = (null)}
2019-06-20 17:21:30.639 fasfsdfs[35506:772778] 结束----<NSThread: 0x6000001dde40>{number = 7, name = (null)}
2019-06-20 17:21:30.639 fasfsdfs[35506:772778] 111----<NSThread: 0x6000001dde40>{number = 7, name = (null)}
分析:
- 串行任务,任务依次执行,异步不会阻塞线程,具备开启线程能力,所以在第二个
async
时会等当前任务执行完成再去执行,由打印的时间和次序对比支持以上结论。
concurrent sync
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
sleep(2);
NSLog(@"结束----%@",[NSThread currentThread]);
});
NSLog(@"0000");
输出:
2019-06-20 17:30:12.118 fasfsdfs[35506:772690] 开始----<NSThread: 0x60000014ee00>{number = 1, name = main}
2019-06-20 17:30:14.118 fasfsdfs[35506:772690] 结束----<NSThread: 0x60000014ee00>{number = 1, name = main}
2019-06-20 17:30:14.118 fasfsdfs[35506:772690] 0000
分析:
- 并发队列任务可以同时执行,同步不具备开启线程能力,会阻塞当前线程,由打印数据可以得出结论。
concurrent sync 任务中嵌套 sync
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"111----%@",[NSThread currentThread]);
});
sleep(2);
NSLog(@"结束----%@",[NSThread currentThread]);
});
输出:
2019-06-20 17:32:13.585 fasfsdfs[35506:772690] 开始----<NSThread: 0x60000014ee00>{number = 1, name = main}
2019-06-20 17:32:13.585 fasfsdfs[35506:772690] 111----<NSThread: 0x60000014ee00>{number = 1, name = main}
2019-06-20 17:32:15.586 fasfsdfs[35506:772690] 结束----<NSThread: 0x60000014ee00>{number = 1, name = main}
分析:
- 并发与串行的差别在这里可以提现到淋漓尽致。如果串行这样执行,必然会造成死锁。
- 执行第一个
sync
时,阻塞主线程,暂停正在执行的任务,转而去执行sync
里面的任务。 - 执行到第二个
sync
时,如果是串行任务之间会互相等待,而并发却不会,新任务不必等待之前的任务执行完;所以第二个 sync会暂停当前任务去执行自己block 里面的任务。 - 任务二执行完成,主线程继续执行接下来的任务,等待两秒,打印输出。
concurrent sync 任务中嵌套 async
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"111----%@",[NSThread currentThread]);
sleep(1);
});
sleep(2);
NSLog(@"结束----%@",[NSThread currentThread]);
});
输出:
2019-06-20 22:21:33.544 fasfsdfs[35506:772690] 开始----<NSThread: 0x60000014ee00>{number = 1, name = main}
2019-06-20 22:21:33.544 fasfsdfs[35506:811697] 111----<NSThread: 0x600000130c80>{number = 11, name = (null)}
2019-06-20 22:21:35.545 fasfsdfs[35506:772690] 结束----<NSThread: 0x60000014ee00>{number = 1, name = main}
分析:
- 如果前面所讲您已经了解明白,我相信这一步不会是问题的。
- sync 主线程执行,async 并发队列开启子线程执行新任务
#######concurrent async
dispatch_queue_t queue = dispatch_queue_create("----", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
sleep(2);
NSLog(@"结束----%@",[NSThread currentThread]);
});
NSLog(@"0000");
输出:
2019-06-20 22:26:57.317 fasfsdfs[35506:772690] 0000
2019-06-20 22:26:57.317 fasfsdfs[35506:822749] 开始----<NSThread: 0x6000001dc140>{number = 17, name = (null)}
2019-06-20 22:26:59.318 fasfsdfs[35506:822749] 结束----<NSThread: 0x6000001dc140>{number = 17, name = (null)}
分析:
- "0000" 打印在 "开始" 之前,我猜测可能是系统内部要创建开辟内存耗时了。
- 异步开启子线程
concurrent async 任务中嵌套 sync
dispatch_async(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"111----%@",[NSThread currentThread]);
sleep(1);
});
sleep(2);
NSLog(@"结束----%@",[NSThread currentThread]);
});
输出:
2019-06-20 22:33:16.687 fasfsdfs[35506:825627] 开始----<NSThread: 0x600000130500>{number = 20, name = (null)}
2019-06-20 22:33:16.687 fasfsdfs[35506:825627] 111----<NSThread: 0x600000130500>{number = 20, name = (null)}
2019-06-20 22:33:19.694 fasfsdfs[35506:825627] 结束----<NSThread: 0x600000130500>{number = 20, name = (null)}
分析:
- async 开启子线程,sync 阻塞线程,去执行自己的任务。
concurrent async 任务中嵌套 async
dispatch_async(queue, ^{
//打印 NSThread为main
NSLog(@"开始----%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"111----%@",[NSThread currentThread]);
sleep(1);
});
sleep(2);
NSLog(@"结束----%@",[NSThread currentThread]);
});
输出:
2019-06-20 22:37:48.393 fasfsdfs[35506:830404] 开始----<NSThread: 0x6000001dee80>{number = 21, name = (null)}
2019-06-20 22:37:48.394 fasfsdfs[35506:830834] 111----<NSThread: 0x6000001def40>{number = 22, name = (null)}
2019-06-20 22:37:50.397 fasfsdfs[35506:830404] 结束----<NSThread: 0x6000001dee80>{number = 21, name = (null)}
分析:
- 执行到第一个 async,因为是并发,开启一个新线程去执行任务。
- 执行到第二个 async,又开启新的线程去执行新的任务。
上面就是 并发/串行, 同步/异步 的各种嵌套分析;如果还有不清楚的读者欢迎私信我。