Swift -Start/Stop 同步操作队列

Swift -Start/Stop Synchronous OperationQueue

我有一些操作需要同步 运行。我试图遵循 this link,但对我的情况来说还不够清楚。

op2 在 op1 完成之前不会启动,而 op3 在 op2 完成之前不会启动,但在此期间我需要能够停止任何操作并重新启动。例如,如果 op2 是 运行ning,我知道它无法停止,但无论出于何种原因,我都需要能够阻止 op3 执行,因为 op1 已重新启动。我该怎么做?

这是一个很简单的例子,实际代码比较复杂

var queue1 = OperationQueue()
var queue2 = OperationQueue()
var queue3 = OperationQueue()
     
var operation1: BlockOperation?
var operation2: BlockOperation?
var operation3: BlockOperation?

// a DispatchGroup has finished running now it's time to start the operations ...
dispatchGroup.notify(queue: .global(qos: .background)) { [weak self] in
    DispatchQueue.main.async { [weak self] in

        self?.runFirstFunc()
    }
}

func runFirstFunc() {

   var count = 0

   let num in arr  {
       count += num
   }

   // now that the loop is finished start the second func but there is a possibility something may happen in the first that should prevent the second func from running
   runSecondFunc(count: count)
}

func runSecondFunc(count: Int) {

    do {

        try ...

        // if the do-try is successful do something with count then start thirdFunc but there is a possibility something may happen in the second func that should prevent the third func from running
        runThirdFunc()

    } catch {
        return
    }
}

func runThirdFunc() {

    // this is the final operation, once it hits here I know it can't be stopped even if I have to restart op1 again but that is fine
}

这是一个未经测试的实现,它允许在任何任务正在执行其子任务(重复)时取消。

如果是第二个任务fails/throws,它会自动从第一个任务重新开始。

如果用户手动停止/启动,最后一个运行中的任务将退出它的执行(尽快)。

注意:您必须根据自己的实现处理 [weak self] 部分。

import Foundation

class TestWorker {
    let workerQueue = DispatchQueue.global(qos: .utility)
    var currentWorkItem: DispatchWorkItem?
    
    func start() {
        self.performTask { self.performTask1() }
    }
    
    func stop() {
        currentWorkItem?.cancel()
    }
    
    func performTask(block: @escaping (() -> Void)) {
        let workItem = DispatchWorkItem(block: block)
        self.currentWorkItem = workItem
        workerQueue.async(execute: workItem)
    }
    
    func performTask1() {
        guard let workItem = self.currentWorkItem else { return }
        func subtask(index: Int) {}
        for i in 0..<100 {
            if workItem.isCancelled { return }
            subtask(index: i)
        }
        self.performTask { self.performTask2() }
    }
    
    func performTask2() {
        guard let workItem = self.currentWorkItem else { return }
        func subtask(index: Int) throws {}
        for i in 0..<100 {
            if workItem.isCancelled { return }
            do { try subtask(index: i) }
            catch { 
                self.start()
                return
            }
        }
        self.performTask { self.performTask3() }
    }
    
    func performTask3() {
        guard let workItem = self.currentWorkItem else { return }
        func subtask(index: Int) {}
        for i in 0..<100 {
            if workItem.isCancelled { return }
            subtask(index: i)
        }
        /// Done
    }
}

也许,这是研究 Swift 合并的一个很好的理由:

  • 将您的任务定义为 Publishers。
  • 使用 flatMap 链接它们,可选择将输出从上一个传递到下一个。
  • 使用switchToLatest重新启动整个事情并取消之前的运行 - 如果有的话。
  • 在订阅者上使用 cancel 取消整个事情。

你说:

op2 doesn't start until op1 is finished and op3 doesn't start until op2 is finished ...

如果使用 OperationQueue,您可以通过创建三个操作并将 op1 定义为 op2 的依赖项并将 op2 定义为 op3 的依赖项来实现。

... but during that time I need to be able to stop any of the operations and restart all over again.

如果使用OperationQueue,如果要停止所有已添加到队列中的操作,调用cancelAllOperations

For example if op2 is running, I know that it cannot be stopped, ...

嗯,这取决于 op2 在做什么。如果它在循环中旋转进行计算,那么,是的,它可以在操作中途取消。您只需检查 isCancelled,如果是,则停止相关操作。或者,如果它是网络请求(或其他可取消的请求),您也可以覆盖 cancel 方法并取消任务。这取决于操作在做什么。

... but for whatever reason I need to be able to prevent op3 from executing because op1 has restarted.

当然,取消了所有使用 cancelAllOperations 的操作后,您可以将三个新操作(及其关联的依赖项)重新添加到队列中。