GCD是iOS开发中非常好用的线程管理方法,本篇博客会简单介绍GCD队列和任务执行方式的概念,以代码实验的方式验证GCD各种执行方式与线程的关系。
GCD的介绍
GCD为Grand Central Dispatch的缩写。
Grand Central Dispatch (GCD)是苹果爸爸开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在iOS 4及以上版本使用。
GCD核心思想
GCD的核心思想 —— 任务 , 队列
队列
队列指的是任务队列, 用来存放任务的队列, 可以说队列是一种比较特殊的线性表, 采用的是先进先出(FIFO)原则, 就像是出高速公路收费站一样, 从最前面一个开始, 排在前面的车子交了费先走, 才到下一个, 这里的队列分为两种,并行队列和串行队列.
并行队列:全名为Concurrent Dispatch Queue, 指的是可以让多个任务同时执行, 如果用到并行队列的话, 是会自动开启多个线程同时执行任务.
串行队列:全名Serial Dispatch Queue, 指的是任务一个接一个的执行, 完成了前面的那个就到后面那个, 和我们刚刚举的收费站例子一样.
使用dispatch_queue_create来创建一个队列,默认情况下创建的为串行队列。如果需要创建并行队列,需要设置DISPATCH_QUEUE_CONCURRENT,设置DISPATCH_QUEUE_SERIAL为串行队列。
注意: 并行队列只有在异步执行(dispatch_async)才有效.
在队列中还有还有两个会经常使用到的队列。
主队列:dispatch_get_main_queue()
主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行。
全局队列:dispatch_get_global_queue(longidentifier,unsignedlongflags)
关于全局队列,生成一个全局队列需要设置两个参数。
第一个参数:用来设置该队列在整个任务执行中的优先级别。
两者的对应关系如下:
- DISPATCH_QUEUE_PRIORITY_HIGH
- DISPATCH_QUEUE_PRIORITY_DEFAULT
- DISPATCH_QUEUE_PRIORITY_LOW
- DISPATCH_QUEUE_PRIORITY_BACKGROUND
此处值得一提的DISPATCH_QUEUE_PRIORITY_BACKGROUND级别,被设置成后台级别的队列,它会等待所有比它级别高的队列中的任务执行完或CPU空闲的时候才会执行自己的任务。例如磁盘的读写操作非常耗时,如果我们不需要立即获取到磁盘的数据,我们可以把读写任务放到后台队列中,这样读写任务只会在恰当的时候去执行而不会影响需要更改优先级的其他任务,整个程序也会更加有效率。
任务
所谓的任务, 就是指我们程序员放在GCD里的操作, 一般是用Block方式进行, 这里有两种执行任务的操作,同步执行和异步执行, 两个的区别就是在是否开启新线程进行操作.
同步执行:在GCD里是sync, 不会开启新线程, 只会在当前线程进行操作.
异步执行:在GCD里是async, 可以另外开启一个新的线程执行任务.
任务执行分为同步、异步,队列类型分为串行、并行,所以搭配如下:
同步 + 串行;
同步 + 并行;
异步 + 串行;
异步 + 并行;
同步 + 串行 的执行,异步 + 并行 的执行方式比较显而易见,所以在此处主要针对同步 + 并行,异步 + 串行 的任务做一下实验验证。
下面我们就开始各种欢乐的实验吧~
同步 + 并行 操作实验
执行结果
结论:丛该实验中确实是证实了并行队列在同步执行中并不会开辟新的线程,所有的任务都是在主线程中完成,并且任务为一个一个的串行执行。
异步 + 串行 操作实验
执行结果
从该实验操作中,可以看到最先开始任务还是在主线程中执行,后来异步执行会开辟一条新的线程用来执行队列中的任务。但是可以看出开辟线程也是会损耗性能,所以在主线程中打印结束标示的任务反而先打印出来。
异步 + 多条串行 实验操作
执行结果
异步 + 串行 的执行结果得到异步执行串行队列,是会开辟新的线程执行任务的,但博主不由想到开辟线程数是否会和队列数有关系,通过 异步 + 多条串行 操作实验可以得出在异步执行情况下,会为每一条串行队列生成一条新的线程执行任务。
异步 + 并行 实验操作
从 异步 + 并行 的执行结果,对比 异步 + 串行 执行结果,在异步执行中一定会开辟新的线程,串行队列开辟了一条新线程供串行队列中的任务一个一个的执行,在并行队列中为了确保队列中的任务能尽快的执行,异步执行情况下为并行队列中的每一个任务都开辟了一条新的线程,因此可以得出节省时间的情况下,异步 + 并行 的方法是很不错,但从打印结果可以看出开辟线程也会消耗处理器的资源。
同步 + 多队列 实验操作
同步 + 多队列 的结果证实了同步情况下,线程数和队列数无关,并不会开辟新的线程执行任务。同步执行方式不会开辟新的线程,那么同步执行会在哪一条线程中进行任务执行呢?为了得到答案,博主又进行了一次下面的实验操作。
异步 + 多队列 中 任务进行 同步 执行 实验操作
异步 + 多队列 中 任务进行 同步 的实验结果可以看出来,在队列queue1的任务中我又擦入了一条并行队列同步执行,系统为queue1队列开辟了线程3来执行任务,因为同步执行不开辟新的线程,因此并行队列queue3的任务也直接在queue1的线程3中执行了,因此可以得出同步执行不开辟线程,同步执行的任务会在他正在执行的线程中执行。
以上做了自己创建的队列在同步,异步执行中的一些情况,可以总结为 异步 + 并行 时,系统会为每个任务开创一个新的线程执行,者异步 + 串行 的情况下,队列数决定了线程数。同步情况下不开辟新的线程。
除了自己创建队列执行任务之外,系统也还存在着主队列、全局队列直接供我们使用。
在上文中,也提到了全局队列dispatch_get_global_queue也属于并行队列,博主做了全局队列异步、同步的执行后,执行结果和自创并行队列无异,也不由思考全局队列和自创并发队列到底有什么卵区别呢?查阅一些资料后,总结如下:
1,全局队列没有名字,但是并发队列有名字。有名字可以便于查看系统日志;
2,全局队列是所有应用程序共享的;
3,在mrc的时候,全局队列不用手动释放,但是并发队列需要;
在使用系统的主队列时有一个值得一提的问题:卡线程
以上图片的写法可以看到,执行结果在执行第一句后就不再执行后文了,这种情况叫做卡线程。大致原因:我们都知道, 同步执行是一个一个任务去执行的。但主线程还在执行“搞事情的打印”的时候, 我们又往主线程里塞任务, 这个时候就会出现异常现象。
优化方式
为了避免卡线程的情况,那么就先开辟一条新的线程,然后再去执行主队列中的任务,这么做打印结果就正常了,并且通过这个写法,也得出一个结论,同步执行主队列时,任务执行不会再保持在原线程里,而是回到了主线程中,主队列中的任务只能在主线程中执行。
总结
通过以上的各项实验,对GCD队列和任务在线程中的执行方式有了进一步了解,队列和任务只是GCD最普通的用法,有时间会在关于GCD的实际使用依然以代码实验的方式写一篇博客作为记录。