(上图是一些 Operation 经常使用的功能的UML图)swift
GCD 是在封装了C,进行多线程开发。而 Operation & OperationQueue 则是封装了 GCD 进行多线程开发。在平时用GCD就能解决不少多线程任务开发的问题, 那为何还要使用 Operation & OperationQueue 呢?安全
使用 Operation & OperationQueue 可以:bash
Operation 一个操做,能够当作一个任务。相似于 GCD 里面的 DispatchItem。Operation 是一个抽象类,里面定义了任务安全执行的逻辑。 在实际使用的时候通常使用系统建立的子类 BlockOperation (Objective-C 还可使用 NSInvocationOperation)。或者建立 Operation 的自定义子类。多线程
OperationQueue 操做的队列,管理操做的执行,相似于 GCD 里面的 DispatchQueue。在 GCD 中,任务的执行顺序是先进先出 FIFO。OperationQueue 执行 Operation 的时候依据 Operation 的优先级和是否在准备就绪的状态。当一个 Operation 加入 OperationQueue 之后,只有等到 Operation 执行完毕(任务取消也是执行完毕)才能从 OperationQueue 里面移除。OperationQueue 不能直接把 Operation 移除。并发
增长一个辅助方法输出当前内容和执行的线程app
func printOperation<T>(_ message: T) {
print(" \(message), thread = \(Thread.current)")
}
复制代码
let operation = BlockOperation {
printOperation("Op init block task 1 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Op init block task 1 end")
}
operation.addExecutionBlock {
printOperation("Op task 2 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Op task 2 end")
}
operation.addExecutionBlock {
printOperation("Op task 3 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Op task 3 end")
}
复制代码
初始化一个 BlockOperation, 随后能够加入多个任务异步
能够定义本身的同步执行子类,和异步执行子类。同步执行子类的建立须要配置的比较少,通常状况只须要建立一个本身的初始化方法和覆盖 main() 方法就行了。异步执行的则比较复杂,须要本身设置多个状态属性。async
class CustomOperation: Operation {
private let address: String
init(address: String) {
self.address = address
}
override func main() {
if isCancelled {
return
}
printOperation("CustomOperation task 1 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("CustomOperation task 1 end")
}
}
复制代码
具体可参考:Apple Operation Documentationide
let operationTask2 = BlockOperation {
printOperation("Operation task 2 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 2 end")
}
let operationTask3 = BlockOperation {
printOperation("Operation task 3 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 3 end")
}
let operationTask4 = BlockOperation {
printOperation("Operation task 4 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 4 end")
}
// 2 -> 4 - > 3
operationTask3.addDependency(operationTask4)
operationTask4.addDependency(operationTask2)
复制代码
在上面的例子中,operationTask4 的开始执行依赖 operationTask2,operationTask3 的开始执行依赖 operationTask4。ui
let operationTask2 = BlockOperation {
printOperation("Operation task 2 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 2 end")
}
let operationTask3 = BlockOperation {
printOperation("Operation task 3 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 3 end")
}
let operationTask4 = BlockOperation {
printOperation("Operation task 4 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 4 end")
}
operationTask2.queuePriority = .veryHigh
operationTask3.queuePriority = .normal
operationTask4.queuePriority = .low
复制代码
在上面设置了 Operation 的队列优先级属性
operation.completionBlock = {
DispatchQueue.main.async {
// Task done
}
}
复制代码
和 GCD 里面的 DispatchWorkItem 同样,任务能够本身执行,也能够提交给队列执行。
本身执行调用 start() 方法。下面看一些例子:
BlockOperation 的单个任务
private func singleBlockOperation() {
printOperation("Test begin")
let operation = BlockOperation {
printOperation("Operation init block task 1 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation init block task 1 end")
}
printOperation("Operation start")
operation.start()
printOperation("Test end")
}
复制代码
输出:
Test begin, thread = <NSThread: 0x6000039ead80>{number = 1, name = main}
Operation start, thread = <NSThread: 0x6000039ead80>{number = 1, name = main}
Operation init block task 1 begin, thread = <NSThread: 0x6000039ead80>{number = 1, name = main}
Operation init block task 1 end, thread = <NSThread: 0x6000039ead80>{number = 1, name = main}
Test end, thread = <NSThread: 0x6000039ead80>{number = 1, name = main}
复制代码
能够看到 start() 默认是一个同步执行方法。它会等待任务在当前线程执行完毕 (例子此时在主线程)。
BlockOperation 的多个任务
状况一:
private func singleBlockMutipleTaskOperation() {
printOperation("Test begin")
let operation = BlockOperation {
printOperation("Operation init block task 1 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation init block task 1 end")
}
operation.addExecutionBlock {
printOperation("Operation task 2 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 2 end")
}
operation.addExecutionBlock {
printOperation("Operation task 3 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 3 end")
}
printOperation("Operation start")
operation.start()
printOperation("Test end")
}
复制代码
输出:
Test begin, thread = <NSThread: 0x6000006b6d00>{number = 1, name = main}
Operation start, thread = <NSThread: 0x6000006b6d00>{number = 1, name = main}
Operation init block task 1 begin, thread = <NSThread: 0x6000006b6d00>{number = 1, name = main}
Operation task 2 begin, thread = <NSThread: 0x600000635d00>{number = 6, name = (null)}
Operation task 3 begin, thread = <NSThread: 0x600000620e80>{number = 7, name = (null)}
Operation task 3 end, thread = <NSThread: 0x600000620e80>{number = 7, name = (null)}
Operation init block task 1 end, thread = <NSThread: 0x6000006b6d00>{number = 1, name = main}
Operation task 2 end, thread = <NSThread: 0x600000635d00>{number = 6, name = (null)}
Test end, thread = <NSThread: 0x6000006b6d00>{number = 1, name = main}
复制代码
此时 BlockOperation 第一个任务 BlockOperation { // task }
在当前线程(当前线程是主线程)执行,额外的任务 addExecutionBlock { // task }
在其它线程执行。
状况二:
private func singleBlockMutipleTaskOperationAddTasks() {
printOperation("Test begin")
let operation = BlockOperation()
operation.addExecutionBlock {
printOperation("Operation task 2 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 2 end")
}
operation.addExecutionBlock {
printOperation("Operation task 3 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 3 end")
}
printOperation("Operation start")
operation.start()
printOperation("Test end")
}
复制代码
输出:
Test begin, thread = <NSThread: 0x6000009c95c0>{number = 1, name = main}
Operation start, thread = <NSThread: 0x6000009c95c0>{number = 1, name = main}
Operation task 2 begin, thread = <NSThread: 0x6000009c95c0>{number = 1, name = main}
Operation task 3 begin, thread = <NSThread: 0x600000959f40>{number = 6, name = (null)}
Operation task 3 end, thread = <NSThread: 0x600000959f40>{number = 6, name = (null)}
Operation task 2 end, thread = <NSThread: 0x6000009c95c0>{number = 1, name = main}
Test end, thread = <NSThread: 0x6000009c95c0>{number = 1, name = main}
复制代码
和状况一的结果相似: 第一个任务 addExecutionBlock { // task1 }
在当前线程(当前线程是主线程)执行,额外的任务 addExecutionBlock { // tasks }
在其它线程执行。
private func queueAddOperations() {
printOperation("Test begin")
let queue = OperationQueue()
queue.addOperation {
printOperation("Operation task 1 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 1 end")
}
let operationTask2 = BlockOperation {
printOperation("Operation task 2 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 2 end")
}
let operationTask3 = BlockOperation {
printOperation("Operation task 3 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 3 end")
}
queue.addOperation(operationTask2)
queue.addOperation(operationTask3)
printOperation("Test end")
}
复制代码
输出:
Test begin, thread = <NSThread: 0x600002bc6cc0>{number = 1, name = main}
Test end, thread = <NSThread: 0x600002bc6cc0>{number = 1, name = main}
Operation task 1 begin, thread = <NSThread: 0x600002b51980>{number = 7, name = (null)}
Operation task 2 begin, thread = <NSThread: 0x600002bb3400>{number = 8, name = (null)}
Operation task 3 begin, thread = <NSThread: 0x600002bc3280>{number = 9, name = (null)}
Operation task 2 end, thread = <NSThread: 0x600002bb3400>{number = 8, name = (null)}
Operation task 3 end, thread = <NSThread: 0x600002bc3280>{number = 9, name = (null)}
Operation task 1 end, thread = <NSThread: 0x600002b51980>{number = 7, name = (null)}
复制代码
异步并不是执行而且全部任务开启新线程。
OperationQueue 里面有一个设置当前队列最大操做并发执行的属性 maxConcurrentOperationCount
private func queueActAsSerial() {
printOperation("Test begin")
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1 // 设置最大运行数目
queue.addOperation {
printOperation("Operation task 1 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 1 end")
}
let operationTask2 = BlockOperation {
printOperation("Operation task 2 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 2 end")
}
let operationTask3 = BlockOperation {
printOperation("Operation task 3 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 3 end")
}
queue.addOperation(operationTask2)
queue.addOperation(operationTask3)
printOperation("Test end")
}
复制代码
输出:
Test begin, thread = <NSThread: 0x600002b66d00>{number = 1, name = main}
Test end, thread = <NSThread: 0x600002b66d00>{number = 1, name = main}
Operation task 1 begin, thread = <NSThread: 0x600002b14080>{number = 3, name = (null)}
Operation task 1 end, thread = <NSThread: 0x600002b14080>{number = 3, name = (null)}
Operation task 2 begin, thread = <NSThread: 0x600002bf7a80>{number = 6, name = (null)}
Operation task 2 end, thread = <NSThread: 0x600002bf7a80>{number = 6, name = (null)}
Operation task 3 begin, thread = <NSThread: 0x600002bf7a80>{number = 6, name = (null)}
Operation task 3 end, thread = <NSThread: 0x600002bf7a80>{number = 6, name = (null)}
复制代码
能够当作此时就像是串行队列同样,只能等上一个任务完成才能执行下一个任务。这个最大并发数目在执行的时候最好是用默认值。它会由系统给出一个最优的值。在实际的设置中若是设置为 maxConcurrentOperationCount = 1000 系统也是会根据实际状况取最优值的。
private func operationDependenciesAndCompletionBlock() {
printOperation("Test begin")
let queue = OperationQueue()
queue.addOperation {
printOperation("Operation task 1 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 1 end")
}
let operationTask2 = BlockOperation {
printOperation("Operation task 2 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 2 end")
}
let operationTask3 = BlockOperation {
printOperation("Operation task 3 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 3 end")
}
let operationTask4 = BlockOperation {
printOperation("Operation task 4 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 4 end")
}
// 2 -> 4 - > 3
operationTask3.addDependency(operationTask4)
operationTask4.addDependency(operationTask2)
operationTask4.completionBlock = {
DispatchQueue.main.async {
printOperation("operationTask4 completion")
}
}
queue.addOperation(operationTask2)
queue.addOperation(operationTask3)
queue.addOperation(operationTask4)
printOperation("Test end")
}
复制代码
输出:
Test begin, thread = <NSThread: 0x600000bbebc0>{number = 1, name = main}
Test end, thread = <NSThread: 0x600000bbebc0>{number = 1, name = main}
Operation task 1 begin, thread = <NSThread: 0x600000b34000>{number = 6, name = (null)}
Operation task 2 begin, thread = <NSThread: 0x600000b2c440>{number = 7, name = (null)}
Operation task 1 end, thread = <NSThread: 0x600000b34000>{number = 6, name = (null)}
Operation task 2 end, thread = <NSThread: 0x600000b2c440>{number = 7, name = (null)}
Operation task 4 begin, thread = <NSThread: 0x600000b2c440>{number = 7, name = (null)}
Operation task 4 end, thread = <NSThread: 0x600000b2c440>{number = 7, name = (null)}
Operation task 3 begin, thread = <NSThread: 0x600000b34000>{number = 6, name = (null)}
operationTask4 completion, thread = <NSThread: 0x600000bbebc0>{number = 1, name = main}
Operation task 3 end, thread = <NSThread: 0x600000b34000>{number = 6, name = (null)}
复制代码
能够看出:
queue.waitUntilAllOperationsAreFinished()
这个会阻塞当前线程,直到队列里面全部 Operation 执行完毕。
private func queueCancel() {
printOperation("Test begin")
let queue = OperationQueue()
queue.addOperation {
printOperation("Operation task 1 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 1 end")
}
let operationTask2 = BlockOperation {
printOperation("Operation task 2 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 2 end")
}
let operationTask3 = BlockOperation {
printOperation("Operation task 3 begin")
Thread.sleep(forTimeInterval: 3)
printOperation("Operation task 3 end")
}
operationTask2.completionBlock = {
DispatchQueue.main.async {
printOperation("operationTask2 completion. isFinished = \(operationTask2.isFinished), isCancelled = \(operationTask2.isCancelled)")
}
}
queue.addOperation(operationTask2)
queue.addOperation(operationTask3)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
printOperation("xxxx cancelAllOperations")
queue.cancelAllOperations()
}
printOperation("Test end")
}
复制代码
输出:
Test begin, thread = <NSThread: 0x600002d1ecc0>{number = 1, name = main}
Operation task 1 begin, thread = <NSThread: 0x600002d86b00>{number = 6, name = (null)}
Operation task 2 begin, thread = <NSThread: 0x600002d8ba40>{number = 7, name = (null)}
Operation task 3 begin, thread = <NSThread: 0x600002d68680>{number = 5, name = (null)}
Test end, thread = <NSThread: 0x600002d1ecc0>{number = 1, name = main}
xxxx cancelAllOperations, thread = <NSThread: 0x600002d1ecc0>{number = 1, name = main}
Operation task 2 end, thread = <NSThread: 0x600002d8ba40>{number = 7, name = (null)}
Operation task 1 end, thread = <NSThread: 0x600002d86b00>{number = 6, name = (null)}
Operation task 3 end, thread = <NSThread: 0x600002d68680>{number = 5, name = (null)}
operationTask2 completion. isFinished = true, isCancelled = true, thread = <NSThread: 0x600002d1ecc0>{number = 1, name = main}
复制代码
经过设置 queue.isSuspended = true // or false
有时候一些关键资源须要保证线程安全,在 GCD 中可使用 DispatchSemaphore 使用,下面添加一个使用 NSLock 的例子
private var tickets = 12; // 12 张票
private var lock: NSLock!
private func queueWithThreadSafe() {
lock = NSLock()
let queue0 = OperationQueue()
let queue1 = OperationQueue()
// 添加画不一样队列的不一样操做
queue0.addOperation {
self.tikcetsSell()
}
queue0.addOperation {
self.tikcetsSell()
}
queue0.addOperation {
self.tikcetsSell()
}
queue1.addOperation {
self.tikcetsSell()
}
}
// 多个线程卖票,须要保证线程安全
private func tikcetsSell() {
while true {
lock?.lock() // 加锁 ..... 在须要保存线程安全的代码前加锁
if tickets > 0 {
Thread.sleep(forTimeInterval: 0.3)
tickets -= 1
printOperation("Tickets count = \(tickets)")
}
lock?.unlock() // 去锁 ..... 在须要保存线程安全的代码后面去锁
if tickets <= 0 {
printOperation("Tickets sold out")
break
}
}
}
复制代码
输出:
Tickets count = 11, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 10, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 9, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 8, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 7, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 6, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 5, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 4, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 3, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 2, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 1, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets count = 0, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets sold out, thread = <NSThread: 0x60000145be80>{number = 7, name = (null)}
Tickets sold out, thread = <NSThread: 0x6000014ba400>{number = 8, name = (null)}
Tickets sold out, thread = <NSThread: 0x6000014b7540>{number = 9, name = (null)}
Tickets sold out, thread = <NSThread: 0x6000014a8440>{number = 10, name = (null)}
复制代码
可使用 NSLock 在 OperationQueue 里面实现线程安全。
这里有一个自定义 Operaion 的实际例子:Raywenderlich: Operation and OperationQueue Tutorial in Swift