老生常谈-GCD线程机制总结
来源:互联网 发布:js中的format函数 编辑:程序博客网 时间:2024/05/29 03:43
使用GCD多线程开发曾经就像会使用block一样,是显得那么的高大上。但如今,在iOS开发中对其的使用早已是司空见惯,家常便饭了,dispatch_async、dispatch_get_main_queue()、dispatch_get_global_queue()、dispatch_queue_create()这些函数早就是在项目里用烂的语句了。不过使用的人是很多,但GCD似乎并不像其显示出的那样“平易近人”,其中涉及到的任务、队列、线程等等一些基本的概念,还总是被隔三差五的在各种文章中被反复提起,反复分析,因为这些概念确实是挺“乱”的,糊涂的人仍然是大多数。最近我又在论坛里看到了不少GCD相关的问题,大家的回答与讨论,对GCD的理解,确实也可以称得上各说各话,百家争鸣了。于是我也想写篇博客,谈谈我对GCD的一些基本概念的理解与总结。
一、一些概念
任务:
简单理解,
dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block)每次调用这两个函数,就会往目标队列中添加一个任务(block中定义的就是一个任务),任务都是被加入到目标队列队尾的.
示例:
NSLog(@"任务1:语句1"); NSLog(@"任务1:语句2"); NSLog(@"任务1:语句3"); dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentQueue, ^{ NSLog(@"任务2:语句1"); NSLog(@"任务2:语句2"); dispatch_sync(concurrentQueue, ^{ NSLog(@"任务3:语句1"); NSLog(@"任务3:语句2"); }); dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"任务4:语句1"); NSLog(@"任务4:语句2"); }); NSLog(@"任务2:语句3"); }); NSLog(@"任务1:语句4");现在主队列中有任务1和任务4,concurrentQueue队列中有任务2和任务3
并行队列与串行队列:
并行队列中的任务执行没有先后关系,可同时进行
串行队列中任务要按在队列中顺序,以任务为单位,先后执行
同步执行与异步执行:
同步执行一个任务会阻塞当前所在线程
异步执行一个任务不会阻塞当前所在线程
二、关于线程的创建
1.使用dispatch_sync同步执行,那么无论使用的串行还是并行队列,系统都不会创建新的线程(任务都会在当前线程中执行)
2.dispatch_async(异步)+串行队列:针对每个串行队列只创建一个线程(以队列为单位)
示例:
1、使用dispatch_sync同步执行
如果你使用的是dispatch_sync同步执行,那么无论使用的串行还是并行队列,系统都不会创建新的线程(任务都会在当前线程中执行)
//主线程 NSLog(@"1:%@",[NSThread currentThread]); dispatch_sync(dispatch_get_global_queue(0, 0), ^{ //主线程 NSLog(@"2:%@",[NSThread currentThread]); }); dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL); dispatch_sync(serialQueue, ^{ //主线程 NSLog(@"3:%@",[NSThread currentThread]); }); dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(concurrentQueue, ^{ //主线程 NSLog(@"4:%@",[NSThread currentThread]); }); dispatch_sync(dispatch_get_global_queue(0, 0), ^{ //主线程 NSLog(@"5:%@",[NSThread currentThread]); }); dispatch_async(dispatch_get_global_queue(0, 0), ^{ //使用了async,创建一子线程 402944 NSLog(@"6:%@",[NSThread currentThread]); dispatch_sync(dispatch_get_global_queue(0, 0), ^{ //没有再启一个线程,仍然在在子线程402944 NSLog(@"7:%@",[NSThread currentThread]); }); //子线程402944 NSLog(@"8:%@",[NSThread currentThread]); });
2、使用dispatch_async异步执行
2.1.dispatch_async+串行队列:针对每个串行队列只创建一个线程
//主线程 NSLog(@"1:%@",[NSThread currentThread]); dispatch_queue_t serialQueue1 = dispatch_queue_create("SerialQueue1", NULL); dispatch_async(serialQueue1, ^{ //针对serialQueue1创建的线程412525 NSLog(@"2:%@",[NSThread currentThread]); }); dispatch_async(serialQueue1, ^{ //针对serialQueue1创建的线程412525 NSLog(@"3:%@",[NSThread currentThread]); }); dispatch_async(serialQueue1, ^{ //针对serialQueue1创建的线程412525 NSLog(@"4:%@",[NSThread currentThread]); }); dispatch_queue_t serialQueue2 = dispatch_queue_create("SerialQueue2", NULL); dispatch_async(serialQueue2, ^{ //针对serialQueue2创建的线程412507 NSLog(@"5:%@",[NSThread currentThread]); }); dispatch_async(serialQueue2, ^{ //针对serialQueue2创建的线程412507 NSLog(@"6:%@",[NSThread currentThread]); }); dispatch_async(serialQueue2, ^{ //针对serialQueue2创建的线程412507 NSLog(@"7:%@",[NSThread currentThread]); }); dispatch_async(dispatch_get_main_queue(), ^{ //主线程,dispatch_get_main_queue是串行队列 NSLog(@"8:%@",[NSThread currentThread]); }); dispatch_async(dispatch_get_main_queue(), ^{ //主线程,dispatch_get_main_queue是串行队列 NSLog(@"9:%@",[NSThread currentThread]); });
2.2.dispatch_async+并行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT); //主线程 NSLog(@"1:%@",[NSThread currentThread]); dispatch_async(concurrentQueue, ^{ //线程1058771 NSLog(@"2:%@",[NSThread currentThread]); }); dispatch_async(concurrentQueue, ^{ //线程1058769 NSLog(@"3:%@",[NSThread currentThread]); }); dispatch_async(concurrentQueue, ^{ //线程1058769 NSLog(@"4:%@",[NSThread currentThread]); }); dispatch_async(concurrentQueue, ^{ //线程1058771 NSLog(@"5:%@",[NSThread currentThread]); }); dispatch_async(concurrentQueue, ^{ //线程1058769 NSLog(@"6:%@",[NSThread currentThread]); }); dispatch_async(concurrentQueue, ^{ //线程1058771 NSLog(@"7:%@",[NSThread currentThread]); });
针对每个任务,系统会根据当前状态,为每个任务创建或分配一个线程,线程不会无限的被增加,会复用线程。
dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentQueue, ^{ NSLog(@"1:%@",[NSThread currentThread]); dispatch_async(concurrentQueue, ^{ NSLog(@"2:%@",[NSThread currentThread]); dispatch_async(concurrentQueue, ^{ NSLog(@"3:%@",[NSThread currentThread]); dispatch_async(concurrentQueue, ^{ NSLog(@"4:%@",[NSThread currentThread]); dispatch_async(concurrentQueue, ^{ NSLog(@"5:%@",[NSThread currentThread]); dispatch_async(concurrentQueue, ^{ NSLog(@"6:%@",[NSThread currentThread]); }); }); }); }); }); });
这个例子同样可得出上面的结论
三、GCD多线程代码示例分析
下面就综合利用上文所介绍的概念内容,通过对一些GCD示例代码的分析,尝试找到分析、理解GCD多线程代码的一套方法
NSLog(@"任务1:语句1"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"任务2:语句1"); }); NSLog(@"任务1:语句2");
任务2被放到了主队列队尾,也就是说任务1中的所有语句执行完成后,才能执行任务2。但是sync会阻塞当前线程直到任务2完成,任务1中的语句2就永远得不到执行。所以程序就陷入了僵局,运行时崩溃。
NSLog(@"任务1:语句1"); dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL); dispatch_async(serialQueue, ^{ //dispatch_async + 串行队列:创建了一个子线程 NSLog(@"任务2:语句1"); dispatch_sync(dispatch_get_main_queue(), ^{//阻塞的是当前子线程 NSLog(@"任务3:语句1"); }); NSLog(@"任务2:语句2"); }); NSLog(@"任务1:语句2");和上面的例子有点儿像,但这次程序顺利执行。任务3被放到了主队列队尾,先执行完任务1的语句2后才能执行任务3。但是这次sync阻塞的不是主线程(而是子线程),没有影响到任务1语句2的执行,程序并未陷入僵局。
NSLog(@"任务1:语句1"); dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL); dispatch_sync(serialQueue, ^{ NSLog(@"任务2:语句1"); }); NSLog(@"任务1:语句2");
程序顺利执行,sync虽然仍然会阻塞主线程,但是任务2是被加到了serialQueue队列中,与任务1并无执行顺序上的关联,所以程序可正常运行。
dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL); NSLog(@"任务1:语句1"); dispatch_async(serialQueue, ^{ //dispatch_async + 串行队列:创建了一个子线程 NSLog(@"任务2:语句1"); dispatch_async(serialQueue, ^{ NSLog(@"任务3:语句1"); }); dispatch_async(serialQueue, ^{ NSLog(@"任务4:语句1"); }); NSLog(@"任务2:语句2"); }); NSLog(@"任务1:语句2");正常运行,任务3和任务4被加到了串行队列serialQueue的任务2之后,但因为是异步执行,并不会阻塞线程,没有影响到任务2的执行,所以运行正常。同时,这个例子也证明了,任务都是被加到队尾的,虽然任务3和任务4是异步执行的,但因为它们处于的是串行队列,且在队尾,所以执行的顺序一定是任务2->任务3->任务4
dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"任务1:语句1"); dispatch_async(concurrentQueue, ^{ NSLog(@"任务2:语句1"); dispatch_sync(concurrentQueue, ^{ NSLog(@"任务3:语句1"); }); NSLog(@"任务2:语句2"); }); NSLog(@"任务1:语句2");程序顺利执行,虽然任务3被加到了concurrentQueue队列的任务2后面,sync也会阻塞主子线程,但是concurrentQueue是并行队列,任务2与任务3并没有执行上的先后顺序,所以程序不会陷入僵局
dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", NULL); NSLog(@"任务1:语句1"); dispatch_async(serialQueue, ^{ //dispatch_async + 串行队列:创建了一个子线程 NSLog(@"任务2:语句1"); dispatch_sync(serialQueue, ^{ NSLog(@"任务3:语句1"); }); NSLog(@"任务2:语句2"); }); NSLog(@"任务1:语句2");
程序崩溃,任务3被放到了serialQueue队列任务2后面,sync阻塞了子线程直到任务3完成,此时任务2:语句2与任务3就陷入了僵局
这些例子还能写很多,但是无论多么复杂,但只要明确以下几个问题(任务+队列+线程),问题就都会迎刃而解了:
1.划分出任务
2.明确任务所属队列
3.明确任务所属队列特点(串行还是并行)
4.明确任务执行所在线程
5.考虑好async(sync)对线程的影响
6.关键,看看线程层面的执行(阻不阻塞)是否对队列任务的调度(串行或并行)造成了影响
- 老生常谈-GCD线程机制总结
- 老生常谈-远程线程注入
- GCD是否创建线程总结
- 老生常谈的“总结”
- 老生常谈之线程与进程
- Linux线程机制总结
- IOS 之 GCD线程 心得总结
- 为你总结老生常谈的
- 线程安全的单例模式 [老生常谈]
- GCD 线程
- GCD 线程
- GCD线程
- GCD线程
- GCD线程
- 线程GCD
- JAVA线程池机制总结
- GCD总结
- GCD总结
- BlockCanary — 轻松找出Android App界面卡顿元凶
- 游戏设计之我见 —— 关卡设计中的固有印象
- 13.8 Swift构造方法的自动继承
- JAVAScript:mobile端,基于transform-origin和tranform(scale),实现表格缩放
- C# Type调用方法和CreateInstance的区别
- 老生常谈-GCD线程机制总结
- Spark简介
- 使用Scala实现文件单词计数
- java ThreadLocal proplem
- Java集合类
- 13.9 Swift必须构造方法
- 汇编指令简介
- Android-UI 特效
- java发送http,内容为xml格式