Grand Central Dispatch用于复杂的流程?

时间:2018-04-26 05:22:19

标签: swift asynchronous grand-central-dispatch completionhandler

我有 a,b,c,d,e 耗时的任务函数和完成处理程序。

它们之间存在约束:

  1. b & c 等待 a 完成
  2. 上一个任务 e 等待 b & c & d 完成
  3. enter image description here

    如果没有任务 d ,我可以像swift一样编写代码(尚未测试)

    let group = DispatchGroup()
    
    group.enter()
    a() { group.leave() }
    group.wait()
    
    group.enter()
    b() { group.leave() }
    
    group.enter()
    c() { group.leave() }
    
    group.notify(queue: .main) {
        e()
    }
    

    如何在不等待 a 的情况下添加任务 d

    于4月30日10:00(+8)编辑

    代码不同表示

      

    最简单的方法是使下载功能同步,并在其文档中添加一条警告,告知它不应该从主线程中调用。

    所以我根据它制作了一个版本。这种方式无法处理并发调用的返回值。但它看起来真的像async / await。所以我现在很满意。谢谢你们。

    async / await like part是

        myQueue.async {
            downloadSync("A")
            downloadSync("B", isConcurrent: true)
            downloadSync("C", isConcurrent: true)
            downloadSync("D", 4, isConcurrent: true)
            waitConcurrentJobs()
            downloadSync("E")
        }
    

    完整的代码如下。

        let myGroup = DispatchGroup()
        let myQueue = DispatchQueue(label: "for Sync/Blocking version of async functions")
    
        func waitConcurrentJobs() {
            myGroup.wait()
        }
    
        // original function (async version, no source code)
        func download(_ something: String, _ seconds: UInt32 = 1, completionHandler: @escaping ()->Void = {}) {
            print("Downloading \(something)")
            DispatchQueue.global().async {
                sleep(seconds)
                print("\(something) is downloaded")
                completionHandler()
            }
        }
    
        // wrapped function (synced version)
        // Warning:
        // It blocks current thead !!!
        // Do not call it on main thread
        func downloadSync(
            _ something: String,
            _ seconds: UInt32 = 1,
            isConcurrent: Bool = false
            ){
            myGroup.enter()
            download(something, seconds) { myGroup.leave() }
            if !isConcurrent {
                myGroup.wait()
            }
        }
    
        // Now it really looks like ES8 async/await
        myQueue.async {
            downloadSync("A")
            downloadSync("B", isConcurrent: true)
            downloadSync("C", isConcurrent: true)
            downloadSync("D", 4, isConcurrent: true)
            waitConcurrentJobs()
            downloadSync("E")
        }
    

    结果

    enter image description here

4 个答案:

答案 0 :(得分:0)

编辑:最简单的方法是使download函数同步,并在其文档中添加一条警告,告知它永远不应该从主线程调用。异步函数的厄运金字塔是coroutines were proposed的原因,除了斯莱夫特的创造者克里斯拉特纳之外。截至2018年4月,它尚未成为等待审核的正式提案,因此很有可能您无法在Swift 5中看到它。

同步下载功能:

// Never call this from main thread
func download(_ something: String, _ seconds: UInt32 = 1, completionHandler: @escaping ()->Void = {}) {
    let group = DispatchGroup()

    print("Downloading \(something)")
    group.enter()
    DispatchQueue.global().async {
        sleep(seconds)
        print("\(something) is downloaded")
        completionHandler()
        group.leave()
    }
    group.wait()
}

NSOperation / NSOperationQueue设置:

let opA = BlockOperation() {
    self.download("A")
}
let opB = BlockOperation() {
    self.download("B")
}
let opC = BlockOperation() {
    self.download("C")
}
let opD = BlockOperation() {
    self.download("D", 4)
}
let opE = BlockOperation() {
    self.download("E")
}

opB.addDependency(opA)
opC.addDependency(opA)

opE.addDependency(opB)
opE.addDependency(opC)
opE.addDependency(opD)

let operationQueue = OperationQueue()
operationQueue.addOperations([opA, opB, opC, opD, opE], waitUntilFinished: false)

答案 1 :(得分:0)

您的原始努力似乎与我非常接近。您可以稍作调整:将BCD设为完成触发E的组。

A可能是另一个小组,但是由于这是一项任务,所以我不明白这一点。完成后触发BC

请注意,与您的问题和其他答案中的某些示例代码不同,在下面的代码中,DA可以立即启动并并行运行。

let q = DispatchQueue(label: "my-queue", attributes: .concurrent)
let g = DispatchGroup()
func taskA() {  print("A")  }
func taskB() {  print("B"); g.leave()  }
func taskC() {  print("C"); g.leave()  }
func taskD() {  print("D"); g.leave()  }
func taskE() {  print("E")  }
g.enter()
g.enter()
g.enter()
q.async {
    taskA()
    q.async(execute: taskB)
    q.async(execute: taskC)
}
q.async(execute: taskD)
g.notify(queue: q, execute: taskE)

答案 2 :(得分:0)

您可以使用此框架实现异步/等待模式-https://github.com/belozierov/SwiftCoroutine

当您调用await时,它不会阻塞线程,而只会挂起协程,因此您也可以在主线程中使用它。

func awaitAPICall(_ url: URL) throws -> String? {
    let future = URLSession.shared.dataTaskFuture(for: url)
    let data = try future.await().data
    return String(data: data, encoding: .utf8)
}

func load(url: URL) {
    DispatchQueue.main.startCoroutine {
        let result1 = try self.awaitAPICall(url)
        let result2 = try self.awaitAPICall2(result1)
        let result3 = try self.awaitAPICall3(result2)
        print(result3)
    }
}

答案 3 :(得分:-1)

我想展示使用Scala之类的替代解决方案:

let result = funcA().flatMap { resultA in
    return [funcB(param: resultA.0),
            funcC(param: resultA.1),
            funcD()]
        .fold(initial: [String]()) { (combined, element) in
            return combined + [element]
    }
}.flatMap { result in
    return funcE(param: result)
}.map { result in
    print(result)
}

基本上就是这样。它处理错误(隐式)并且是线程安全的。 No Operation子类;)

注意,仅当A成功完成时才会调用funcD。由于funcA()可能会失败,因此调用它是没有意义的。但是,如果需要,代码可以很容易地进行调整以使其成为可能。

请将此与我使用Dispatch Groups and Dispatch Queues的其他解决方案中的foo()功能进行比较。

下面是异步函数定义的示例,每个函数都将结果传递给下一个函数:

func funcA() -> Future<(String, String)> {
    print("Start A")
    let promise = Promise<(String, String)>()
    DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
        print("Complete A")
        promise.complete(("A1", "A2"))
    }
    return promise.future
}

func funcB(param: String) -> Future<String> {
    print("Start B")
    let promise = Promise<String>()
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        print("Complete B")
        promise.complete("\(param) -> B")
    }
    return promise.future
}

func funcC(param: String) -> Future<String> {
    print("Start C")
    let promise = Promise<String>()
    DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
        print("Complete C")
        promise.complete("\(param) -> C")
    }
    return promise.future
}

func funcD() -> Future<String> {
    print("Start D")
    let promise = Promise<String>()
    DispatchQueue.global().asyncAfter(deadline: .now() + 4) {
        print("Complete D")
        promise.complete("D")
    }
    return promise.future
}

func funcE(param: [String]) -> Future<String> {
    print("Start E")
    let promise = Promise<String>()
    DispatchQueue.global().asyncAfter(deadline: .now() + 4) {
        print("Complete E")
        promise.complete("\(param) -> E")
    }
    return promise.future
}

将其打印到控制台:

Start A Complete A Start B Start C Start D Complete B Complete C Complete D Start E Complete E ["A1 -> B", "A2 -> C", "D"] -> E

提示:有几个Future和Promise库可用。