How can you use Dispatch Groups to wait to call multiple functions that depend on different data?

☆樱花仙子☆ 提交于 2020-02-21 04:54:15

问题


I have three variables, a, b and c. I have three asynchronous functions with completion blocks to update these variables and three more functions that do some work with only some of the data.

I'm making sure that the working functions wait until all the data is updated with a DispatchGroup.

// The Data
var a: String?
var b: String?
var c: String?

// The Update
let group = DispatchGroup()

group.enter()
updateA() {
    group.leave()
}

group.enter()
updateB() {
    group.leave()
}

group.enter()
updateC() {
    group.leave()
}

group.wait()

// The work
doSomthingWith(a: a, b: b)
doSomethingElseWith(b: b, c: c)
doAnotherThingWith(a: a, c: c)

What I'd like to be able to do is call each work function once the parameters have been updated, rather than waiting for everything. This is only a (obviously) simplified version. There could be many more variables and functions.

I'm using Swift. Many thanks in advance.


回答1:


To achieve that with dispatch groups alone you would need three dispatch groups which are entered and left accordingly:

let abGroup = DispatchGroup()
let bcGroup = DispatchGroup()
let acGroup = DispatchGroup()

abGroup.enter()
abGroup.enter()
bcGroup.enter()
bcGroup.enter()
acGroup.enter()
acGroup.enter()

// When a is updated:
abGroup.leave()
acGroup.leave()

// When b is updated:
abGroup.leave()
bcGroup.leave()

// When c is updated:
acGroup.leave()
bcGroup.leave()

Then you can wait for the completion of each group independently

abGroup.notify(queue: .main) {
    // Do something with a and b
}
bcGroup.notify(queue: .main) {
    // Do something with b and c
}
acGroup.notify(queue: .main) {
    // Do something with a and c
}

However, this does not scale well with more tasks and dependencies.

The better approach is to add Operations to an OperationQueue, that allows to add arbitrary dependencies:

let queue = OperationQueue()

let updateA = BlockOperation {
    // ...
}
queue.addOperation(updateA)

let updateB = BlockOperation {
    // ...
}
queue.addOperation(updateB)

let updateC = BlockOperation {
    // ...
}
queue.addOperation(updateC)

let doSomethingWithAandB = BlockOperation {
    // ...
}
doSomethingWithAandB.addDependency(updateA)
doSomethingWithAandB.addDependency(updateB)
queue.addOperation(doSomethingWithAandB)

let doSomethingWithBandC = BlockOperation {
    // ...
}
doSomethingWithBandC.addDependency(updateB)
doSomethingWithBandC.addDependency(updateC)
queue.addOperation(doSomethingWithBandC)

let doSomethingWithAandC = BlockOperation {
    // ...
}
doSomethingWithAandC.addDependency(updateA)
doSomethingWithAandC.addDependency(updateC)
queue.addOperation(doSomethingWithAandC)

For asynchronous request with completion handlers you can use a (local) dispatch group inside each block operation to wait for the completion.

Here is a self-contained example:

import Foundation

var a: String?
var b: String?
var c: String?

let queue = OperationQueue()

let updateA = BlockOperation {
    let group = DispatchGroup()
    group.enter()
    DispatchQueue.global().asyncAfter(deadline: .now() + 1.0, execute: {
        a = "A"
        group.leave()
    })
    group.wait()
    print("updateA done")
}
queue.addOperation(updateA)

let updateB = BlockOperation {
    let group = DispatchGroup()
    group.enter()
    DispatchQueue.global().asyncAfter(deadline: .now() + 2.0, execute: {
        b = "B"
        group.leave()
    })
    group.wait()
    print("updateB done")
}
queue.addOperation(updateB)

let updateC = BlockOperation {
    let group = DispatchGroup()
    group.enter()
    DispatchQueue.global().asyncAfter(deadline: .now() + 3.0, execute: {
        c = "C"
        group.leave()
    })
    group.wait()
    print("updateC done")
}
queue.addOperation(updateC)

let doSomethingWithAandB = BlockOperation {
    print("a=", a!, "b=", b!)
}
doSomethingWithAandB.addDependency(updateA)
doSomethingWithAandB.addDependency(updateB)
queue.addOperation(doSomethingWithAandB)

let doSomethingWithAandC = BlockOperation {
    print("a=", a!, "c=", c!)
}
doSomethingWithAandC.addDependency(updateA)
doSomethingWithAandC.addDependency(updateC)
queue.addOperation(doSomethingWithAandC)

let doSomethingWithBandC = BlockOperation {
    print("b=", b!, "c=", c!)
}
doSomethingWithBandC.addDependency(updateB)
doSomethingWithBandC.addDependency(updateC)
queue.addOperation(doSomethingWithBandC)

queue.waitUntilAllOperationsAreFinished()

Output:

updateA done
updateB done
a= A b= B
updateC done
a= A c= C
b= B c= C


来源:https://stackoverflow.com/questions/50026983/how-can-you-use-dispatch-groups-to-wait-to-call-multiple-functions-that-depend-o

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!