iOS并发:如何使用OperationQueue代替barrier?

iOS concurrent: how to use OperationQueue instead of barrier?

学习OperationQueue时,是否可以使用OperationQueue代替gcd barrier

情况如下:

upload 3 images , then upload other 3 images

barriergcd 完美搭配

    func workFlow(){
        let queue = DispatchQueue(label: "test.concurrent.queue", qos: .background, attributes: .concurrent, autoreleaseFrequency: .workItem)
        queue.async {
            self.uploadImg(idx: "A_0")
        }
        queue.async {
            Thread.sleep(forTimeInterval: 2)
            self.uploadImg(idx: "A_1")
        }
        queue.async {
            self.uploadImg(idx: "A_2")
        }
        queue.async(qos: queue.qos, flags: .barrier) {
            print("group A done")
        }
        print("A: should not be hanged")
        queue.async {
            self.uploadImg(idx: "B_0")
        }
        queue.async {
            self.uploadImg(idx: "B_1")
        }
        queue.async {
            self.uploadImg(idx: "B_2")
        }
        
        queue.async(qos: queue.qos, flags: .barrier) {
            print("group B done")
        }
        print("B: should not be hanged")
    }
    
    
    func uploadImg(idx info: String){
        Thread.sleep(forTimeInterval: 1)
        print("img \(info) uploaded")
    }

OperationQueue相比,这里有一点小瑕疵

主队列被挂起,检查打印

"A/B: should not be hanged"

  lazy var uploadQueue: OperationQueue = {
       var queue = OperationQueue()
       queue.name = "upload queue"
       queue.maxConcurrentOperationCount = 5
       return queue
     }()


   func workFlow(){
        let one = BlockOperation {
            self.uploadImg(idx: "A_0")
        }
        let two = BlockOperation {
            Thread.sleep(forTimeInterval: 3)
            self.uploadImg(idx: "A_1")
        }
        let three = BlockOperation {
            self.uploadImg(idx: "A_2")
        }
        uploadQueue.addOperations([one, two, three], waitUntilFinished: true)
        print("A: should not be hanged")

        uploadQueue.addOperation {
            print("group A done")
        }
        let four = BlockOperation {
            self.uploadImg(idx: "B_0")
        }
        let five = BlockOperation {
            self.uploadImg(idx: "B_1")
        }
        let six = BlockOperation {
            self.uploadImg(idx: "B_2")
        }
        uploadQueue.addOperations([four, five, six], waitUntilFinished: true)
        print("B: should not be hanged")
        uploadQueue.addOperation {
            print("group B done")
        }
    }

如何使用 OperationQueue 做得更好?

如果不希望加入队列的操作阻塞当前线程,waitUntilFinished必须是false。但是如果你设置为true,它会阻塞当前线程,直到添加的操作完成。

显然,如果你不等待,它不会阻塞主线程,但你也会失去屏障行为。但是 iOS 13 和 macOS 10.15 引入了 addBarrierBlock。如果你真的需要障碍并且必须支持更早的 OS 版本,那么你将不得不使用依赖项。但是,如果您之前使用 GCD 屏障只是为了限制并发程度,那么 maxConcurrentOperationCount 可能会使屏障变得毫无意义。这完全取决于您为什么要对这些 uploads/downloads 使用障碍。 (看到 upload/download 队列的障碍有点不寻常,因为它会降低效率。)

How to do it better with OperationQueue?

我假设 uploadImg 同步下载图像。我会将其重构为自己的 Operation 子类,它执行必要的 Operation KVO,例如 。这包含了运行中的下载任务,但您也可以对上传或数据任务执行相同的操作(尽管数据任务对内存的影响要大得多)。

但始终建议避免同步网络请求,以 (a) 确保不占用工作线程; (b) 使请求可取消。