swift 使用未命名的async let并发运行异步任务

lb3vh1jj  于 2023-05-05  发布在  Swift
关注(0)|答案(2)|浏览(145)

有了Swift并发,有没有可能有一个类似“未命名”的async let
这里有一个例子。你有以下演员:

actor MyActor {
    private var foo: Int = 0
    private var bar: Int = 0

    func setFoo(to value: Int) async {
        foo = value
    }

    func setBar(to value: Int) async {
        bar = value
    }

    func printResult() {
        print("foo =", foo)
        print("bar =", bar)
    }
}

现在我想使用给定的方法设置foobar。简单用法如下所示:

let myActor = MyActor()
await myActor.setFoo(to: 5)
await myActor.setBar(to: 10)
await myActor.printResult()

但是,此代码是按顺序运行的。出于所有意图和目的,假设setFoo(to:)setBar(to:)可能是一个长时间运行的任务。你也可以假设这些方法是互斥的(不共享变量,也不会相互影响)。
要使此代码成为当前代码,可以使用async let。然而,这只是启动任务,直到它们稍后被await艾德。在我的例子中,你会注意到我不需要这些方法的返回值。我所需要的只是在调用printResult()之前,前面的任务已经完成。
我可以得出以下结论:

let myActor = MyActor()
async let tempFoo: Void = myActor.setFoo(to: 5)
async let tempBar: Void = myActor.setBar(to: 10)
let _ = await [tempFoo, tempBar]
await myActor.printResult()

显式地创建这些任务,然后对它们的数组执行await操作似乎是不正确的。这真的是最好的办法吗?

vojdkbi0

vojdkbi01#

这可以通过使用withTaskGroup(of:returning:body:)的任务组来实现。方法调用是单独的任务,然后我们awaitwaitForAll(),当所有任务都完成时继续。
验证码:

await withTaskGroup(of: Void.self) { group in
    let myActor = MyActor()

    group.addTask {
        await myActor.setFoo(to: 5)
    }
    group.addTask {
        await myActor.setBar(to: 10)
    }

    await group.waitForAll()
    await myActor.printResult()
}

另一种方法是这样的:

let myActor = MyActor()
await withTaskGroup(of: Void.self) { group in
    group.addTask {
        await myActor.setFoo(to: 5)
    }
    group.addTask {
        await myActor.setBar(to: 10)
    }
}
await myActor.printResult()
5rgfhyps

5rgfhyps2#

我为您的actor创建了一个类,以允许这两个方法的并发执行。

import Foundation

final class Jeep {
    private var foo: Int = 0
    private var bar: Int = 0

    func setFoo(to value: Int) {
        print("begin foo")
        foo = value
        sleep(1)
        print("end foo \(value)")
    }

    func setBar(to value: Int) {
        print("begin bar")
        bar = value
        sleep(2)
        print("end bar \(bar)")
    }

    func printResult() {
        print("printResult foo:\(foo), bar:\(bar)")
    }
}

let jeep = Jeep()
let blocks = [ 
    { jeep.setFoo(to: 1) }, 
    { jeep.setBar(to: 2) },
]

// ...WORK

RunLoop.current.run(mode: RunLoop.Mode.default, before: NSDate(timeIntervalSinceNow: 5) as Date)

将WORK替换为以下内容之一:

// no concurrency, ordered execution
for block in blocks {
    block() 
}
jeep.printResult()

// concurrency, unordered execution, tasks created upfront programmatically
Task {
    async let foo: Void = blocks[0]()
    async let bar: Void = blocks[1]()
    await [foo, bar]
    jeep.printResult()
}

// concurrency, unordered execution, tasks created upfront, but started by the system (I think)
Task {
    await withTaskGroup(of: Void.self) { group in
        for block in blocks {
            group.addTask { block() }
        } 
    }
    // when the initialization closure exits, all child tasks are awaited implicitly
    jeep.printResult()
}

// concurrency, unordered execution, awaited in order
Task {
    let tasks = blocks.map { block in
        Task { block() } 
    }
    for task in tasks {
        await task.value
    }
    jeep.printResult()
}

// tasks created upfront, all tasks start concurrently, produce result as soon as they finish
let stream = AsyncStream<Void> { continuation in
    Task {
        let tasks = blocks.map { block in
            Task { block() }
        }
        for task in tasks {
            continuation.yield(await task.value)
        }
        continuation.finish()
    }
}
Task {
    // now waiting for all values, bad use of a stream, I know
    for await value in stream { 
        print(value as Any)
    }
    jeep.printResult()
}

相关问题