licc

心有猛虎 细嗅蔷薇

0%

详解Swift多线程

Swift终于在5.x版本变得稳定,先来看看Swift5.1中的GCD如何使用

  • 队列

串行队列

串行队列一般只分配一个线程,队列如果有任务执行是不允许插队。
串行队列中执行任务的线程不允许被当前队列中的任务阻塞(死锁),但是能被其他对列阻塞

默认创建的是串行队列

let queue = DispatchQueue(label: "com.youdao.queueName")

主线程就是串行队列

DispatchQueue.main

常见的主线程死锁

//main Threed
print(1)
DispatchQueue.main.sync {
print(2)
}
print(3)
(并行队列) 并发队列

并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。

并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。

Erlang 之父 Joe Armstrong 的解释

创建并发队列

let taskD_queue = DispatchQueue.init(label: "com.youdao.concurrent", attributes: .concurrent)

系统并发队列

let globalQueue = DispatchQueue.global()
更多参数
public convenience init(label: String, qos: DispatchQoS = .unspecified, attributes: DispatchQueue.Attributes = [],
autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit, target: DispatchQueue? = nil)

qos

Quality of Service(服务质量)
队列执行有优先级,优先级由高到低

  • userInteractive: 用户交互任务(例如动画,事件处理或更新应用程序的用户界面)的服务
  • userInitiated:用户发起的动作需要立即得到结果 如打开文档,显示电子邮件
  • default:默认的
  • utility:用户不主动关注的,一些耗时操作。下载或者导入数据,utility任务一般有一个进度条让用户能看到
  • background:最低优先级 后台运行。建立索引 清理文件

默认值为 default

还有一个特殊的unspecified

它表示当前没有QoS信息,系统应该根据环境自动推断QoS信息。如果使用遗弃的API,线程有可能会脱离QoS,这个时候,线程就是unspecified QoS类别的

因此系统并发队列有6个

let globalQueue1 = DispatchQueue.global(qos: .userInteractive)
let globalQueue2 = DispatchQueue.global(qos: .userInitiated)
let globalQueue5 = DispatchQueue.global() //qos default
let globalQueue3 = DispatchQueue.global(qos: .utility)
let globalQueue4 = DispatchQueue.global(qos: .background)
//❌ 不存在这种
let globalQueue4 = DispatchQueue.global(qos: .unspecified)

要注意,在Low Power Mode下,qos为background的队列将被暂停

attributes

//Set
public struct Attributes : OptionSet


public static let concurrent: DispatchQueue.Attributes

@available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
public static let initiallyInactive: DispatchQueue.Attributes
  • concurrent:标记为并发队列
  • initiallyInactive:标识队列中的任务需要手动触发,由队列activate()方法触发。未标记initiallyInactive的队列会自动触发任务

不标明此参数,默认是串行队列。

//串行队列
let task_queue_ser = DispatchQueue.init(label: "com.youdao.queue")

//并发队列
let task_queue_concurrent = DispatchQueue.init(label: "com.youdao.queue", attributes: .concurrent)

//手动触发
let task_queue_initial = DispatchQueue.init(label: "com.youdao.queue", attributes: .initiallyInactive)
task_queue_initial.active()

//手动触发的并发队列
let task_queue_combine = DispatchQueue.init(label: "com.youdao.queue", attributes: [.concurrent, .initiallyInactive])
task_queue_combine.active()

autoreleaseFrequency

autoreleaseFrequency是枚举,标明autoreleasepool的释放频率

 public enum AutoreleaseFrequency {
//继承目标队列
case inherit
//跟随每个任务的执行周期进行自动创建和释放
@available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
case workItem
//不创建autoreleasepool
@available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
case never

....
}

自己创建的队列,默认值是 .inherit

系统定义的全局队列,此属性是 .never

如果任务中需要大量创建对象,可以设置为.never,然后手动创建autoreleasepool

target

设置一个队列的目标队列,讲当前队列中的任务放到目标队列中取执行。目标队列最终约束了队列任务的优先级。

所有手动创建的队列,target最终都指向了系统自带的队列。主队列或者全局并发队列。

为什么不直接加到系统队列,而是要自定义队列呢?这样的好处是可以将任务分组管理。如单独阻塞某个队列中的任务,而不是阻塞系统队列中的全部任务。如果阻塞了系统队列,所有指向它的原队列也就被阻塞。

设置target可以在DispatchQueue初始化时候指定。

let task_queue = DispatchQueue.init(label: "abc", qos: .default, attributes: [.concurrent], autoreleaseFrequency: .workItem, target: DispatchQueue.global())

也可以指定attributes为.initiallyInactive。手动触发前使用setTarget更改指定的target

let task_queue = DispatchQueue.init(label: "abc", qos: .default, attributes: [.initiallyInactive, .concurrent], autoreleaseFrequency: .workItem, target: DispatchQueue.global())

task_queue.setTarget(queue: DispatchQueue.global(qos: .background))

可以思考下,下方代码会输出什么。

let task_queue = DispatchQueue.init(label: "abc", qos: .default, attributes: [.initiallyInactive, .concurrent], autoreleaseFrequency: .workItem, target: DispatchQueue.main)

task_queue.async {
for k in 0..<10 {
print( "A" + String(k))
}
}

task_queue.async {
for k in 0..<10 {
print( "B" + String(k))
}
}

task_queue.activate()

Group