当今App中多线程是必不可少的因素,而GCD起着举足轻重的作用。
下面我们来看看swift 3.0中的GCD,能给我们带来哪些惊喜吧!
dispatch_async
GCD最常用的模式就是在全局队列执行任务主线程中刷新UI
DispatchQueue.global(qos: .default).async {
print("task" + "\(Thread.current)")
//主线程异步执行 刷新UI
DispatchQueue.main.async {
print("UI - task" + "\(Thread.current) ")
}
}
Queue attributes
由于.serial不在是DispatchQueue.Attributes中的属性,只留下.concurrent。下面我们通过代码验证一下
//创建并发队列
let concurrentQueue = DispatchQueue(label: "mkiltech.com",attributes: .concurrent)
//创建串行队列
let serialQueue = DispatchQueue(label: "mkiltech.com")
//串行异步
serialQueue.async {
sleep(2)
print("serialTask 1" + "\(Thread.current) ")
}
serialQueue.async {
print("serialTask 2" + "\(Thread.current) ")
}
//并发异步
concurrentQueue.async {
sleep(2)
print("concurrentTask 1" + "\(Thread.current) ")
}
concurrentQueue.async {
print("concurrentTask 2" + "\(Thread.current) ")
}
// 打印结果
concurrentTask 2<NSThread: 0x60800007d640>{number = 3, name = (null)}
concurrentTask 1<NSThread: 0x60800007e340>{number = 4, name = (null)}
serialTask 1<NSThread: 0x600000078f00>{number = 5, name = (null)}
serialTask 2<NSThread: 0x600000078f00>{number = 5, name = (null)}
可以看出,默认情况下创建的是串行队列,指定.concurrent为并发队列。
下面来看看队列完整的初始化方法
public convenience init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)
@param qos 队列优先级
新的队列优先级替换了在iOS8中被废弃的旧的优先级属性,显得更加直观友好,这是在 QOS中的调用列表:
* - DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED (.userInitiated)
* - DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT (.default)
* - DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY (.utility)
* - DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND (.background)
@param attributes 串行队列并发队列,上面已说明
@param autoreleaseFrequency
下面是一段官方代码注释,由于没有找到相关官方文档说明,理解可能有偏差。
/*!
* @typedef dispatch_autorelease_frequency_t
* Values to pass to the dispatch_queue_attr_make_with_autorelease_frequency()
* function.
*
* @const DISPATCH_AUTORELEASE_FREQUENCY_INHERIT
* Dispatch queues with this autorelease frequency inherit the behavior from
* their target queue. This is the default behavior for manually created queues.
*
* @const DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM
* Dispatch queues with this autorelease frequency push and pop an autorelease
* pool around the execution of every block that was submitted to it
* asynchronously.
* @see dispatch_queue_attr_make_with_autorelease_frequency().
*
* @const DISPATCH_AUTORELEASE_FREQUENCY_NEVER
* Dispatch queues with this autorelease frequency never set up an individual
* autorelease pool around the execution of a block that is submitted to it
* asynchronously. This is the behavior of the global concurrent queues.
*/
public enum AutoreleaseFrequency {
case inherit
case workItem
case never
}
我们看到DispatchQueue.AutoreleaseFrequency
有三种属性值.inherit
、.workItem
和.never
,稍微研究了一下GCD
和Autorelease
以前,DispatchQueues
将在未指定的时间(当线程变为不活动时)弹出它们的自动释放池。 在实践中,这意味着您为每个提交的调度项目创建了一个自动释放池,或者您的自动释放的对象将挂起不可预测的时间量。
非确定性不是什么好事情(特别是在并发库!),所以现在Apple允许你指定三种行为之一:
.inherit
:不确定,之前默认的行为也是现在的默认值
.workItem
:为每个执行的项目创建和排除自动释放池,项目完成时清理临时对象
.never
:GCD不为您管理自动释放池
DispatchTime 延时执行
let delay = DispatchTime.now() + .seconds(60)
DispatchQueue.main.asyncAfter(deadline: delay) {
// task 延时执行
}
也可以直接+
一个秒数
let two = DispatchTime.now() + 2.0
因为DispatchTime
中自定义了这些运算符。
public func +(time: DispatchWallTime, seconds: Double) -> DispatchWallTime
public func -(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTime
public func <(a: DispatchTime, b: DispatchTime) -> Bool
public func ==(a: DispatchTime, b: DispatchTime) -> Bool
DispatchGroup
如果想在dispatch_queue
中所有的任务执行完成后再做某种操作可以使用DispatchGroup
。原先的dispatch_group_t
由现在的DispatchGroup
对象代替。
let group = DispatchGroup()
let queueDownload1 = DispatchQueue(label: "com.Download1")
queueDownload1.async(group: group) {
// up task 1
}
let queueDownload2 = DispatchQueue(label: "com.Download2")
queueDownload2.async(group: group) {
// up task 2
}
group.notify(queue: DispatchQueue.main) {
// 等待下载完成
}
DispatchGroup
会在组里的操作都完成后执行notify
。
DispatchWorkItem
使用DispatchWorkItem
代替原来的dispatch_block_t
。 在DispatchQueue
执行操作除了直接传了一个() -> Void
类型的闭包外,还可以传入一个DispatchWorkItem
。
public func sync(execute workItem: DispatchWorkItem)
public func async(execute workItem: DispatchWorkItem)
DispatchWorkItem
的初始化方法可以配置Qos
和DispatchWorkItemFlags
,但是这两个参数都有默认参数,所以也可以只传入一个闭包。
public init(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, block: @escaping @convention(block) () -> ())
DispatchWorkItemFlags
枚举中assignCurrentContext
表示QoS
根据创建时的context
决定。 值得一提的是DispatchWorkItem
也有wait
方法,使用方式和group
一样。调用会等待这个workItem
执行完。
let myQueue = DispatchQueue(label: "mkil.queue", attributes: .concurrent)let workItem = DispatchWorkItem { sleep(1) print("done")}myQueue.async(execute: workItem)print("before waiting")workItem.wait()print("after waiting")
barrier
假设我们有一个并发的队列用来读写一个数据对象。如果这个队列里的操作是读的,那么可以多个同时进行。如果有写的操作,则必须保证在执行写入操作时,不会有读取操作在执行,必须等待写入完成后才能读取,否则就可能会出现读到的数据不对。在之前我们用dipatch_barrier
实现。 现在属性放在了DispatchWorkItemFlags
里。
let wirte = DispatchWorkItem(flags: .barrier) {
// write data
}
let dataQueue = DispatchQueue(label: "data", attributes: .concurrent)
dataQueue.async(execute: wirte)
DispatchSemaphore
为了线程安全的统计数量,我们会使用信号量作计数。原来的dispatch_semaphore_t
现在用DispatchSemaphore
对象表示。 初始化方法只有一个,传入一个Int
类型的数。
let semaphore = DispatchSemaphore(value: 5)
// 信号量减一
semaphore.wait()
//信号量加一
semaphore.signal()
dispatch_once
在Swift3中,dispatch_once被废弃了,应该被替换成了其他全局或者静态变量和常量.
class MyManager {
public static let shareInstance = MyManager()
}
let manager = MyManager.shareInstance
dispatch_assert
这个也是今年苹果在OS发布的新技术,线程的先决条件.这个在你执行代码前可以检查当前线程是否是你希望的线程.
enum DispatchPredicate {
//Predicates that the evaluated context is the associated dispatch queue.
case onQueue(DispatchQueue)
//Predicates that the evaluated context is the associated dispatch queue as part of a barrier operation.
case onQueueAsBarrier(DispatchQueue)
//Predicates that the evaluated context is not the associated dispatch queue.
case notOnQueue(DispatchQueue)
// onQueueAsBarrier
let dataQueue = DispatchQueue(label: "data", attributes: .concurrent)
let wirte = DispatchWorkItem(flags: .barrier) {
dispatchPrecondition(condition: .onQueueAsBarrier(dataQueue))
// write data
}
dataQueue.async(execute: wirte)
let queue = DispatchQueue.global()
let mainQueue = DispatchQueue.main
// .notOnQueue
mainQueue.async {
dispatchPrecondition(condition: .notOnQueue(mainQueue))
// This code won't execute
}
// onQueue
queue.async {
dispatchPrecondition(condition: .onQueue(queue))
// This code will execute
}
}
参考文档
https://developer.apple.com/videos/play/wwdc2016/720/
http://gold.xitu.io/post/57f6677e128fe100544dc3cb
https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md