IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue

时间:2022-06-26 16:10:25

1.前言


 iOS中处理多核并发的技术有两种分别是:`Grand Central Dispatch`(以下简称`GCD`)和`NSOperationQueue`框架。iOS开发的老司机们在程序开发中处理多个任务同时执行的时候,一定都会使用到这两个框架,而且GCD依靠它简洁的语法和对block的运用一直很受大家的青睐。ios开发中你一定明白 这样一条原则:“任何用于界面ui刷新和用户交互的操作都要放在主线程来操作,任何耗时或者耗CPU的任务必须在异步线程去操作*”,----小白都会问为什要这样,老司机都说记住就好-------这里就简单解释下:  

首先我们来解释第一句话:“任何用于界面ui刷新和用户交互的操作都要放在主线程来操作”,要明白这句话只要明白下面几个点:1.主线程是线程安全的--把所有ui刷新以及用户的交互放在主线程操作会避免很多意外情况的发生,保证在获取服务端返回的数据时,ui界面可以及时安全的刷新数据,给用户带来良好体验 。2.ios中只有主线程才可以立刻刷新ui界面,如果放在异步线程去操作都会造成线程阻塞和延迟的问题。---第二点“任何耗时或者耗CPU的任务必须在异步线程去操作”---如果你很好的明白了前半句,那么这句话的意思就很好理解了,把耗时或者消耗cpu的操作放在异步线程,也就是为了防止线程的阻塞延迟,防止主线程上的ui刷新和用户操作的一系列动作出现卡顿,死锁,延迟等问题。


2.正文


言归正传,我们继续往下看,如果你对ios的中的GCD和DispatchQueue 使用很熟练的话,那么swift3.0的语法和使用应该就是轻车熟路,如果你还不是特别明白GCD是什么鬼?没关系,这里先给大家来点山里的干货:

1. `dispatch queue`:一堆在主线程(或后台线程)上同步(或异步)来执行的代码,一旦被创建出来,操作系统就开始接手管理,在CPU上分配时间片来执行队列内的代码。开发者没法参与`queue`的管理。队列采用`FIFO模式`(先进先出),意味着先加入的也会被先完成,这和超市排队买单,队伍前的总是最先买单出去,的道理是一样一样的。

2. `work item`:一段代码块,可以在queue创建的时候添加,也可以单独创建方便之后复用,你可以把它当成将要在`queue`上运行的一个代码块。`work items`也遵循着`FIFO模式`,也可以同步(或异步)执行,如果选择同步的方式,运行中的程序直到代码块完成才会继续之后的工作,相对的,如果选择异步,运行中的程序在触发了代码块就立刻返回了。

3. `serial`(串行)vs`concurrent`(并行):`serial`将会执行完一个任务才会开始下一个,`concurrent`触发完一个就立即进入下一个,而不管它是否已完成。

接下来直奔主题:Swift3.0 中的GCD和DispatchQueue 使用。


1.'serial'(串行) vs 'concurrent'(并行)

   1.1 创建一个DispatchQueue的方法:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
就是这么简单????

  label:后面是一个标识,可以随便写,一般建议写成你的工程的dns的反序比较好。

   1.2 接下来我们创建一个串行的queue 和 在主线程中执行的代码对比下看看串行队列和主线程的区别?

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
????

然后我们在viewdidload中执行这个方法,看下控制台打印的结果:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
????

从结果中我们可以看到两个方法是一个一个按顺序来执行的,也就是说串行队列和主线程一样都是串行输出的。换句话说也就是:主线程也是一个串行队列。

那异步(并行)队列执行会是怎么样呢?

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
异步

看下输出结果:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
异步结果

这次我们惊喜地发现和上次不同,并且主线程的函数和异步队列的函数是交替执行 也就是说二者同步的输出,这是因为:异步队列不会阻塞当前线程 而是会另开一个线程来执行当前的任务,而主线程上的任务也就不会被阻塞,所以二者是同步输出的。


通过上面的对比我们至少可以明白两个件事:

1. 使用async主线程和后台线程可以并行执行任务

2. 使用sync则只能串行执行任务, 当前线程被卡住直到串行任务完成才继续


2.GCD服务等级--Qos队列


弄明白'serial'(串行) 和'concurrent'(并行)的关系,我们继续来看下GCD的qos队列。

GCD服务等级(GCD QoS):确定任务重要和优先级的属性

QoS是个基于具体场景的枚举类型,在初始队列时,可以提供合适的QoS参数来得到相应的权限,如果没有指定QoS,那么初始方法会使用队列提供的默认的QoS值

QoS等级(QoS classes),从前到后,优先级从高到低:

userInteractive

userInitiated

default

utility

background

unspecified

从上面的介绍 我们猜想 肯定是 ---等级越高的队列越先被执行,同一等级下的队列中 串行队列肯定是一个一个执行,异步队列肯定是分线程并行执行---下面我们就来验证下:

2.1 首先创建一个Qos队列:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
qos队列

qos:需要传一个DispatchQoS的枚举类型

2.2 同个队列同等级串行输出对比

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
qosConcurrentQueues

看下输出结果:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
qosConcurrentQueues-log

仔细观察 我们会发现 结果并没有像我们猜想的那样 同等级下的异步队列 并行输出,这是怎么回事呢?因为Qos队列默认是串行执行的,所以即使qos队列中的方法是异步的 也会被顺序串行执行。那么怎样才可以并行执行呢?这就要用到Qos队列的另外一个属性:attributes:.concurrent

具体写法:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
同等级并行
IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
 

看下具体函数和运行结果:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
同等级并行-log1

 

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
同等级并行-log2

通过输出结果我们发现 结果是并行输出了,但是细心的你是不是发现了结果中的一丝丝不同,附上的两张结果图,是同一个函数运行多次显示的不同结果,为什么会不同呢?难道程序有问题,一个函数怎么可能有两个结果呢?是不是顿时一脸懵逼,一万个*在心中奔腾????????????????????????????????????????????????????????????????----施主息怒,待山里娃慢慢给你分析:

首先我们要明白系统所谓的并行执行,并不全是我们想象那的样是固定的像log1中的一样十分规矩的输出,内部是会发生资源的倾斜或者顺序的不确定性的,继续看下去后面的例子你定会彻底明白。

我们新建一个不同等级的Qos队列来看下结果:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
不同等级qos

输出结果:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
不同等级qos-log3

观察结果 ,我们会发现并不是我们之前猜测的 等级越高的队列会快速先执行完,而是高等级和低等级的交叉进行输出,仔细想下 其实这就是上面那个问题的完美诠释,系统会使优先级更高的`queue1`比`queue2`更快被执行,虽然在`queue1`运行的时候`queue2`得到一个运行的机会,系统还是将资源倾斜给了被标记为更重要的`queue1`,等`queue1`内的任务全部被执行完成,系统才开始全心全意服务于`queue2` 。---看到这里应该明白了吧。

大家可以在思考个问题,在这些等级中 main queue主线程队列是排在哪个等级呢?下面我们来看个例子:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
main queue 对比

结果:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
结果

从中我们可以清楚的得出结论:`main queue`默认就有一个很高的权限。


接下来我们在来看下Qos队列attributes的另外一个属性,initiallyInactive (不活跃的),我们可以创建一个qos的不活跃队列,这个队列的特点是 需要调用DispatchQueue类的`activate()`让任务执行。看下具体代码:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
initiallyInactive 队列

重点在下面,viewdidload中调用:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
initiallyInactive 队列 调用

看下输出结果:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
initiallyInactive 队列-log

我们发现队列是串行输出的,那么怎样创建一个并行的initiallyInactive 队列呢?查看api我们会发现Qos队列的attributes接收的是一个数组,所以聪明的你肯定知道怎么办吧????

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
initiallyInactive并行 队列  

3.延迟执行


当你的应用的某个流程中的某项任务延迟执行,GCD允许你执行某个方法来达到特定时间后运行你指定任务的目的。直接上代码:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
延迟加载

这里解释下:.now() 方法 是获取当前时间。


4.DispatchWorkItem


DispatchWorkItem是一个代码块,它可以被分到任何的队列,包含的代码可以在后台或主线程中被执行,简单来说:它被用于替换我们前面写的代码block来调用。使用起来也很简单,看代码:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
DispatchWorkItem

5.主线程更新UI


最后给大家分享一个开篇说到的主线程刷新ui的例子,先看代码:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
主线程更新UI

使用的时候 记得在viewDidLoad 中调用setingIMage() 这个方法。

这里主要想和大家介绍下swift中的懒加载和oc中的异同,个人总结了下大家可以参阅:

IOS - 总结下swift使用GCD 多线程(二)GCD和DispatchQueue
懒加载


作者:山_里_娃
链接:http://www.jianshu.com/p/737233208d40
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。