iOS并发编程:如何使用OperationQueue替代GCD barrier实现分组任务
问题根因
现有代码阻塞主线程的核心原因是调用addOperations(_:waitUntilFinished:)时传入了waitUntilFinished = true,这会阻塞当前调用线程(通常为主线程)直到所有传入的操作全部执行完成,和GCD barrier的非阻塞效果完全不符。
解决方案
OperationQueue原生支持操作依赖,完全可以替代GCD barrier实现「分组串行、组内并行」的需求,全程不会阻塞当前线程:
lazy var uploadQueue: OperationQueue = { var queue = OperationQueue() queue.name = "upload queue" queue.maxConcurrentOperationCount = 5 return queue }() func workFlow(){ // 1. 创建A组上传操作 let a0 = BlockOperation { self.uploadImg(idx: "A_0") } let a1 = BlockOperation { Thread.sleep(forTimeInterval: 3) self.uploadImg(idx: "A_1") } let a2 = BlockOperation { self.uploadImg(idx: "A_2") } // 2. 创建组A完成回调操作,依赖所有A组操作执行完成 let groupADone = BlockOperation { print("group A done") } [a0, a1, a2].forEach { groupADone.addDependency($0) } // 3. 创建B组上传操作,所有B组操作依赖「组A完成」操作执行完成 let b0 = BlockOperation { self.uploadImg(idx: "B_0") } let b1 = BlockOperation { self.uploadImg(idx: "B_1") } let b2 = BlockOperation { self.uploadImg(idx: "B_2") } [b0, b1, b2].forEach { $0.addDependency(groupADone) } // 4. 创建组B完成回调操作,依赖所有B组操作执行完成 let groupBDone = BlockOperation { print("group B done") } [b0, b1, b2].forEach { groupBDone.addDependency($0) } // 5. 所有操作一次性加入队列,无需等待,主线程直接往下执行 uploadQueue.addOperations([a0, a1, a2, groupADone, b0, b1, b2, groupBDone], waitUntilFinished: false) // 这两句会立刻输出,不会被阻塞 print("A: should not be hanged") print("B: should not be hanged") } func uploadImg(idx info: String){ Thread.sleep(forTimeInterval: 1) print("img \(info) uploaded") }
效果说明
- 组内的A0/A1/A2会并行执行,全部完成后才会执行
groupADone,之后B0/B1/B2才会并行执行,全部完成后执行groupBDone,和GCD barrier实现的逻辑完全一致 - 所有操作加入队列后立刻返回,不会阻塞主线程,两个print语句会在操作调度前就输出,符合预期
如果你的上传操作实际是异步网络请求,系统提供的BlockOperation无法正确识别异步任务的完成状态,需要自定义Operation子类,手动管理任务的isFinished/isExecuting状态标识即可。
内容的提问来源于stack exchange,提问作者DNG
相关产品推荐
相关产品推荐

