简体   繁体   English

具有依赖性的异步任务执行

[英]Async tasks execution with dependency

Situation:情况:

I have 2 tasks says T1 & T2 in async background mode.我有 2 个任务在异步后台模式下显示T1T2 T2 depends on T1 and have successBlock which is executes after the completion of the both tasks T1 & T2 . T2依赖于T1并且有successBlock它在两个任务T1T2完成后执行。

Quick diagram is below for better understanding.为了更好地理解,快速图表如下。

在此处输入图片说明

Edit:编辑:

To better understanding the tasks, you can assume T1 and T2 are the API calls which always be going to execute in async mode.为了更好地理解任务,您可以假设 T1 和 T2 是始终以异步模式执行的 API 调用。 I need some output data from T1 to hit T2 API.我需要一些来自 T1 的输出数据来命中 T2 API。 After the completion of the both tasks I need to update UI.完成这两个任务后,我需要更新 UI。


To accomplish this scenario, I have added my first async work in T1 and second work in T2 and dependency of T2 to T1 and successblock have dependency on both tasks.为了完成这个场景,我在T1 中添加了我的第一个异步工作,在T2 中添加了第二个工作,并且T2T1和 successblock 的依赖依赖于这两个任务。

Code Work代码工作

  1. My Tasks我的任务

    class TaskManager { static let shared = TaskManager() func task1Call(complete: @escaping ()->()) { DispatchQueue.global(qos: .background).async { for i in 0...10 { print("~~> Task 1 Executing ..", i) sleep(1) } complete() } } func task2Call(complete: @escaping ()->()) { DispatchQueue.global(qos: .background).async { for i in 0...10 { print("==> Task 2 Executing ..", i) sleep(1) } complete() } } }
  2. Execute Tasks执行任务

    class Execution { // Managing tasks with OperationQueue func executeTaskWithOperation() { let t1 = BlockOperation { TaskManager.shared.task1Call { print("Task 1 Completed") } } let t2 = BlockOperation { TaskManager.shared.task2Call { print("Task 2 Completed") } } let successBlock = BlockOperation { print("Tasks Completed") } let oper = OperationQueue() t2.addDependency(t1) successBlock.addDependency(t2) successBlock.addDependency(t1) oper.addOperations([t1, t2, successBlock], waitUntilFinished: true) } } let e = Execution() e.executeTaskWithOperation()

Issue:问题:

Both tasks are executing parallelly and successBlock executes before the completion of task 1 and task 2.两个任务并行执行,且successBlock在任务 1 和任务 2 完成之前执行。

Console Output:控制台输出:

==> Task 2 Executing .. 0
Tasks Completed
~~> Task 1 Executing .. 0
~~> Task 1 Executing .. 1
==> Task 2 Executing .. 1
==> Task 2 Executing .. 2
~~> Task 1 Executing .. 2
==> Task 2 Executing .. 3
~~> Task 1 Executing .. 3
==> Task 2 Executing .. 4
~~> Task 1 Executing .. 4
==> Task 2 Executing .. 5
~~> Task 1 Executing .. 5
==> Task 2 Executing .. 6
~~> Task 1 Executing .. 6
==> Task 2 Executing .. 7
~~> Task 1 Executing .. 7
==> Task 2 Executing .. 8
~~> Task 1 Executing .. 8
==> Task 2 Executing .. 9
~~> Task 1 Executing .. 9
~~> Task 1 Executing .. 10
==> Task 2 Executing .. 10
Task 1 Completed
Task 2 Completed

I unable to figure out what wrong I am doing, even same code work fines when I use sync mode instead of async.我无法弄清楚我在做什么错,当我使用同步模式而不是异步模式时,即使是相同的代码也能正常工作。

Your t1 and t2 are block operations that spawn background threads (which each do some printing and then exit, but it doesn't matter).您的t1t2是产生后台线程的块操作(每个线程都进行一些打印然后退出,但这并不重要)。 Once they finish spawning, they're considered completed.一旦它们完成产卵,它们就被视为已完成。 successBlock depends on the two background threads being spawned, and then it's done. successBlock取决于产生的两个后台线程,然后就完成了。 You want the work in the BlockOperation itself:您想要BlockOperation本身的工作:

class Execution {

    // Managing tasks with OperationQueue
    func executeTaskWithOperation()  {

      let t1 = BlockOperation {
          for i in 0...10 {
              print("~~> Task 1 Executing ..", i)
              sleep(1)
          }
          print("Task 1 completed")
        }

        let t2 = BlockOperation {
          for i in 0...10 {
            print("==> Task 2 Executing ..", i)
            sleep(1)
          }
         print("Task 2 Completed")
        }

        let successBlock = BlockOperation {
            print("Tasks Completed")
        }

        let oper = OperationQueue()

        t2.addDependency(t1)  // Remove this to see concurrent exec of t1 and t2
        successBlock.addDependency(t2)
        successBlock.addDependency(t1)

        oper.addOperations([t1, t2, successBlock], waitUntilFinished: true)

    }
}

let e = Execution()
e.executeTaskWithOperation()

Edit: For execution on a background thread, override Operation .编辑:要在后台线程上执行,请覆盖Operation

class AsyncOp: Operation {
  let task: String
  var running = false
  var done = false

  init(_ task: String) {
    self.task = task
  }

  override var isAsynchronous: Bool { true }
  override var isExecuting: Bool {
    get { running }
    set {
      willChangeValue(forKey: "isExecuting")
      running = newValue
      didChangeValue(forKey: "isExecuting")
    }
  }
  override var isFinished: Bool {
    get { done }
    set {
      willChangeValue(forKey: "isFinished")
      done = newValue
      didChangeValue(forKey: "isFinished")
    }
  }

  override func main() {
    DispatchQueue.global(qos: .background).async {
      self.isExecuting = true
      for i in 0...10 {
        print("\(self.task) Executing ..", i)
        sleep(1)
      }
      print("Done")
      self.isExecuting = false
      self.isFinished = true
    }
  }

  override func start() {
    print("\(task) starting")
    main()
  }
}

class Execution {
  // Managing tasks with OperationQueue
  func executeTaskWithOperation()  {
    let t1 = AsyncOp("task1")
    let t2 = AsyncOp("task2")
    let successBlock = BlockOperation {
      print("Tasks Completed")
    }

    let oper = OperationQueue()
    t2.addDependency(t1)
    successBlock.addDependency(t2)
    successBlock.addDependency(t1)
    oper.addOperations([t1, t2, successBlock], waitUntilFinished: true)
  }
}

let e = Execution()
e.executeTaskWithOperation()

After Joshua's comment , I able to conclude the answer.约书亚的评论之后,我能够得出答案。


Execution changed from OperationQueue to DispatchGroup and DispatchSemaphore .执行从OperationQueue更改为DispatchGroupDispatchSemaphore

DispatchGroup : It makes sure both task tasks are done and then it calls notify block. DispatchGroup :它确保完成两个任务任务,然后调用notify块。

DispatchSemaphore : It holds the async resource with wait command until we wont send the signal command ie we are saying to semaphore to hold yourself until the task1 is not completed. DispatchSemaphore :它使用等待命令保存异步资源,直到我们不会发送信号命令,即我们告诉信号量保持自己直到 task1 未完成。

Sample code of tasks.任务示例代码。

class Execution {
    // Managing tasks with DispatchGroup

    func executeTaskWithGroup() {
        let groups = DispatchGroup()
        let semaphore = DispatchSemaphore(value: 1)
        groups.enter()
        semaphore.wait()
        TaskManager.shared.task1Call {
            groups.leave()
            semaphore.signal()
        }

        groups.enter()
        TaskManager.shared.task2Call {
            groups.leave()
        }

        groups.notify(queue: DispatchQueue.global(qos: .background)) {
            print("Tasks Completed")
        }

    }

}

To execute command all we need to do is.要执行命令,我们需要做的就是。

let e = Execution()
e.executeTaskWithGroup()

But above code is executed in the main thread and block the UI.但是上面的代码在主线程中执行并阻塞UI。 To prevent this you need to call above piece of code in background queue like below.为了防止这种情况,您需要在后台队列中调用上面的代码,如下所示。

let queue = DispatchQueue.init(label: "MyQueue", qos: .background, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)

queue.async {
    let e = Execution()
    e.executeTaskWithGroup()
}

Now everything works fine as per my needed.现在一切正常,按照我的需要。


AddOn添加在

In case, if someone requirement is to call multiple API along with the above scenario then add your tasks in async in the queue.如果有人要求在上述场景中调用多个 API,则将您的任务异步添加到队列中。

let queue = DispatchQueue.init(label: "MyQueue", qos: .background, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)

queue.async {
    let e1 = Execution()
    e1.executeTaskWithGroup()
}

queue.async {
    let e2 = Execution()
    e2.executeTaskWithGroup()
}

Now both e1 and e2 executes parallelly without blocking main thread.现在e1e2并行执行而不会阻塞主线程。


References :参考 :

Dexecutor for the rescue here Dexecutor在这里进行救援

Disclaimer: I am the owner of Dexecutor免责声明:我是 Dexecutor 的所有者

Dexecutor can be used easily for workflow like use case Deexecutor 可以很容易地用于像用例这样的工作流

在此处输入图片说明

Here is sample Application这是示例应用程序

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM