DispatchWorkItem:
DispatchWorkItem是一个任务的封装对象,可以复用,类比Operation,它有一些自己的想法,定义如下:
public init(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, block: @escaping @convention(block) () -> Void)
public func perform()
public func wait()
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
public func notify(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, queue: DispatchQueue, execute: @escaping @convention(block) () -> Void)
public func notify(queue: DispatchQueue, execute: DispatchWorkItem)
public func cancel()
public var isCancelled: Bool { get }
它可以自己执行、等待、通知别的队列、取消等,它是GCD执行任务的最终对象,用Block传入的任务最后也是包装成DispatchWorkItem。一般情况下,不会单独用它来执行任务的,需要配合DispatchQueue和DispatchGroup。
DispatchGroup:
假如现在有一个任务A,需要依赖三个任务A1、A2、A3,此时我们就需要DispatchGroup了,它可以把A1、A2、A3装到组里,等3个任务全部完成以后,通知任务A。代码类似这样:
let group = DispatchGroup.init()
//任务A1
DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
//沉睡两秒,模拟耗时任务
Thread.sleep(forTimeInterval: 2)
print("完成任务A1\(Thread.current)")
}))
//任务A2
DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
//沉睡两秒,模拟耗时任务
Thread.sleep(forTimeInterval: 2)
print("完成任务A2\(Thread.current)")
}))
//任务A3
DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
//沉睡两秒,模拟耗时任务
Thread.sleep(forTimeInterval: 3)
print("完成任务A3\(Thread.current)")
}))
//通知任务A,三个任务已经全部完成
group.notify(queue: DispatchQueue.main) {
print("全部完成\(Thread.current)")
}
// group.wait()
//
// DispatchQueue.main.async(group: group, execute: DispatchWorkItem.init(block: {
// print("全部完成\(Thread.current)")
// }))
//任务B
print("异步任务不会阻塞线程")
打印结果如下:
由于是异步任务,不会阻塞线程,任务B首先完成,然后加入到group的异步任务分别创建线程完成任务,全部完成以后,再来通知任务A。
DispatchSemaphore:
现在来增加点有趣的内容,加入任务A1、A2、A3里再开启异步任务,比如网络请求,代码如下:
let group = DispatchGroup.init()
//任务A1
DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
//开启异步任务
DispatchQueue.global().async {
//模拟网络请求两秒后返回
Thread.sleep(forTimeInterval: 2)
print("完成网络请求A1\(Thread.current)")
}
print("完成任务A1\(Thread.current)")
}))
//任务A2
DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
//开启异步任务
DispatchQueue.global().async {
//模拟网络请求两秒后返回
Thread.sleep(forTimeInterval: 2)
print("完成网络请求A2\(Thread.current)")
}
print("完成任务A2\(Thread.current)")
}))
//任务A3
DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
//开启异步任务
DispatchQueue.global().async {
//模拟网络请求两秒后返回
Thread.sleep(forTimeInterval: 2)
print("完成网络请求A3\(Thread.current)")
}
print("完成任务A3\(Thread.current)")
}))
//通知任务A,三个任务已经全部完成
group.notify(queue: DispatchQueue.main) {
print("全部完成\(Thread.current)")
}
// group.wait()
//
// DispatchQueue.main.async(group: group, execute: DispatchWorkItem.init(block: {
// print("全部完成\(Thread.current)")
// }))
//任务B
print("异步任务不会阻塞线程")
此时的执行顺序会怎样呢?
这个时候,任务A1、A2、A3都提前返回了,网络请求什么时候返回也不一定,如果我们需要依靠网络请求返回的结果,这个时候该怎么办呢?这个时候DispatchSemaphore就该闪亮登场了。DispatchSemaphore的定义如下:
open class DispatchSemaphore : DispatchObject {
}
/// dispatch_semaphore
extension DispatchSemaphore {
public func signal() -> Int
public func wait()
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
}
extension DispatchSemaphore {
/*!
* @function dispatch_semaphore_create
*
* @abstract
* Creates new counting semaphore with an initial value.
*
* @discussion
* Passing zero for the value is useful for when two threads need to reconcile
* the completion of a particular event. Passing a value greater than zero is
* useful for managing a finite pool of resources, where the pool size is equal
* to the value.
*
* @param value
* The starting value for the semaphore. Passing a value less than zero will
* cause NULL to be returned.
*
* @result
* The newly created semaphore, or NULL on failure.
*/
@available(iOS 4.0, *)
public /*not inherited*/ init(value: Int)
}
DispatchSemaphore的使用很简单:
初始化一个开始的量,wait()方法使信号量-1,当信号量变成0,阻塞当前线程,等待信号量大于0,恢复线程。signal()方法使信号量+1,配合wait()可以控制线程并发量。
来看看怎么用DispatchSemaphore来让网络任务同步:
let group = DispatchGroup.init()
//任务A1
DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
//创建信号量为0
let semaphore = DispatchSemaphore.init(value: 0)
//开启异步任务
DispatchQueue.global().async {
//模拟网络请求两秒后返回
Thread.sleep(forTimeInterval: 2)
print("完成网络请求A1\(Thread.current)")
semaphore.signal()
}
//调用wait()方法,此时信号量为0,会阻塞当前线程,任务A1不会返回,所以就不会通知任务A
semaphore.wait()
print("完成任务A1\(Thread.current)")
}))
//任务A2
DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
//创建信号量为0
let semaphore = DispatchSemaphore.init(value: 0)
//开启异步任务
DispatchQueue.global().async {
//模拟网络请求两秒后返回
Thread.sleep(forTimeInterval: 2)
print("完成网络请求A2\(Thread.current)")
semaphore.signal()
}
//调用wait()方法,此时信号量为0,会阻塞当前线程,任务A2不会返回,所以就不会通知任务A
semaphore.wait()
print("完成任务A2\(Thread.current)")
}))
//任务A3
DispatchQueue.global().async(group: group, execute: DispatchWorkItem.init(block: {
//创建信号量为0
let semaphore = DispatchSemaphore.init(value: 0)
//开启异步任务
DispatchQueue.global().async {
//模拟网络请求两秒后返回
Thread.sleep(forTimeInterval: 2)
print("完成网络请求A3\(Thread.current)")
semaphore.signal()
}
//调用wait()方法,此时信号量为0,会阻塞当前线程,任务A3不会返回,所以就不会通知任务A
semaphore.wait()
print("完成任务A3\(Thread.current)")
}))
//通知任务A,三个任务已经全部完成
group.notify(queue: DispatchQueue.main) {
print("全部完成\(Thread.current)")
}
// group.wait()
//
// DispatchQueue.main.async(group: group, execute: DispatchWorkItem.init(block: {
// print("全部完成\(Thread.current)")
// }))
//任务B
print("异步任务不会阻塞线程")
来看看打印结果:
网络请求要先返回,才会完成任务A1、A2、A3,然后才会通知任务A,同样的,任务B最先完成。
总结:
通过DispatchGroup和DispatchSemaphore可以给任务分组,把异步任务变成同步任务,组合使用效果更佳。