在多线程编程中我们总是拿 Operation 和 GCD 做比较 ,OperationQueue 的任务单元是 Operation
,但事实上 GCD 也有类似的任务单元 DispatchWorkItem
,OperationQueue 在类似于取消、添加依赖等功能都由 Operation
提供,这其中显然忽略了和它很像的 DispatchWorkItem
存在,本文的主要内容就是对比这两者的差异。
start() vs perform()
基本的执行功能,两者都是可以独立执行的,并不一定需要依赖 OperationQueue 或是 DispatchQueue
1
2
3
4
let operation = BlockOperation {
print(#function)
}
operation.start()
1
2
3
4
let workItem = DispatchWorkItem {
print(#function)
}
workItem.perform()
cancel() vs cancel()
同样的都有取消功能,当然也有相应的isCancelled
属性对应判断任务是否已经被取消
1
2
3
4
5
6
7
8
9
10
11
12
13
14
lazy var workItem: DispatchWorkItem = {
return DispatchWorkItem {
sleep(2)
guard self.workItem.isCancelled == false else {
return
}
print(#function)
}
}()
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async(execute: workItem)
}
试着在两秒前取消任务,就不会有打印结果
1
2
3
@IBAction func buttonAction(_ sender: UIButton) {
workItem.cancel()
}
waitUntilFinished() vs wait()
首先看waitUntilFinished()
官方的解释
Blocks execution of the current thread until the operation object finishes its task.(阻塞当前线程直到任务完成)
注意这里的当前线程,并不是任务执行的线程(主线程除外),举个例子:
1
2
3
4
5
6
7
8
9
10
override func viewDidLoad() {
super.viewDidLoad()
let operation = BlockOperation {
Thread.sleep(forTimeInterval: 2)
print(Thread.current)
}
OperationQueue().addOperation(operation)
operation.waitUntilFinished()
print(#function)
}
operation 的任务会在子线程等待两秒后完成,虽然任务丢到子线程执行,但主线程还是会被阻塞直到任务完成。
1
2
3
4
5
------主线程---------—————————————被阻塞————————————--------------->
|--------子线程执行任务-------->|
是不是很像同步操作,只不过任务的执行的被移到了其他线程。DispatchWorkItem 极其类似,这里就不做过多介绍。
1
2
3
4
5
6
let workItem = DispatchWorkItem {
print(#function)
}
DispatchQueue.global().async(execute: workItem)
workItem.wait()
但 DispatchWorkItem 除了wait()
还提供两个方法,用于判断任务是否超时。
1
2
3
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
同样看个例子,这里很简单,任务的执行的时间至少是两秒,设置的判断条件是一秒,所以会超时。
1
2
3
4
5
6
7
8
9
10
11
12
13
let workItem = DispatchWorkItem {
Thread.sleep(forTimeInterval: 2)
print(#function)
}
DispatchQueue.global().async(execute: workItem)
let result = workItem.wait(timeout: .now() + 1)
switch result {
case .success:
print("success")
case .timedOut:
print("timedOut")
}
另外关于两个 timeout 参数: DispatchTime
可以理解为相对时间或是应用内时间,DispatchWallTime
则是绝对时间或是世界时间。测试方法很简单,执行下面两个延时执行的例子,之后调整系统时间到五分钟后第二个回调会立即被执行。
1
2
3
4
5
6
DispatchQueue.main.asyncAfter(deadline: .now() + 300) {
print(#function)
}
DispatchQueue.main.asyncAfter(wallDeadline: .now() + 300) {
print(#function)
}
addDependency(:) vs notify(::)
很多开发者会认为 OperationQueue 比起 GCD 在添加任务的依赖操作方面会更优雅甚至觉得 GCD 无法添加任务依赖,事实并非如此,通过 notify(queue: DispatchQueue, execute: DispatchWorkItem)
依然可以很优雅的添加依赖。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let workItem1 = DispatchWorkItem {
Thread.sleep(forTimeInterval: 1)
print(1)
}
let workItem2 = DispatchWorkItem {
print(2)
}
let workItem3 = DispatchWorkItem {
print(3)
}
workItem1.notify(queue: .global(), execute: workItem3)
workItem2.notify(queue: .global(), execute: workItem1)
DispatchQueue.global().async(execute: workItem2)
但试想一下,如果有个需求是想让 workItem1
最后执行,但不管其他任务的调用顺序,Operation 的实现方式就很简单和优雅,DispatchWorkItem 就无法做到这一点。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let operation1 = BlockOperation {
Thread.sleep(forTimeInterval: 1)
print(1)
}
let operation2 = BlockOperation {
print(2)
}
let operation3 = BlockOperation {
print(3)
}
operation1.addDependency(operation2)
operation1.addDependency(operation3)
OperationQueue().addOperations([operation1, operation2, operation3], waitUntilFinished: false)
当然 DispatchGroup 也可以轻松满足这种需求,不过这个并不在本文的讨论范围内。
completionBlock: (() -> Void)? vs notify(::: () -> Void)
两个都可以设置一个完成回调
1
2
3
workItem1.notify(queue: .global()) {
print("\(Thread.current)" + "1")
}
1
2
3
operation1.completionBlock = {
print(#function)
}
qualityOfService vs qos
Operation 在初始化后可以设置 qualityOfService,这个属性的作用在这里就不做过多的介绍
1
operation.qualityOfService = .userInitiated
同样的 DispatchWorkItem 在初始化的时候 qos 参数就同等于 qualityOfService
1
2
3
DispatchWorkItem(qos: .userInitiated, flags: .barrier) {
}
至此 DispatchWorkItem 的所有方法都在本文中出现过,Operation 还有以下这些属性或方法没有在 DispatchWorkItem 找到对应的实现
1
2
3
4
5
6
7
8
9
10
open func main()
open var isExecuting: Bool { get }
open var isFinished: Bool { get }
open var isConcurrent: Bool { get }
open var isAsynchronous: Bool { get }
open var isReady: Bool { get }
open func removeDependency(_ op: Operation)
open var dependencies: [Operation] { get }
open var queuePriority: Operation.QueuePriority
open var name: String?
可以看到 Operation 额外提供了很多状态属性、移除依赖方法以及任务的优先级。所以单纯对比 DispatchWorkItem 和 Operation 来说,所谓的 GCD 的任务无法取消是错误的,OperationQueue 在添加任务依赖方面确实有优势。