【问题标题】:How does await in swift work with tuples?swift中的等待如何处理元组?
【发布时间】:2022-01-04 03:32:43
【问题描述】:

我正在努力确保我了解await 的行为。假设我们有以下函数:

func do() async {
  //code
}
func stuff() async {
  //code
}

以下语句将依次运行dostuff

await do()
await stuff()

但是下面的语句会并行运行dostuff 对吗?

await (do(), stuff())

如果我的代码是并行运行还是按顺序运行,我不确定如何签入 Xcode。

【问题讨论】:

  • 一种快速的方法是编写单元测试并在元组上设置不同的超时。使第一个超时为 1 秒,第二个没有超时,第二个打印“second”,第一个打印“first”
  • 我的直觉说它们是平行的,但我很好奇。

标签: swift async-await concurrency parallel-processing


【解决方案1】:

看来,为了获得并发执行,您必须为每个函数有一个明确的async let

actor A {
    var t = 1
    func tt() -> Int {
        for i in 0 ... 1000000 {
            t += i
        }
        let s = t
        t = 1
        return s
    }
}
var a = A()
var b = A()
func go() {
    Task {
        var d = Date()
        await (a.tt(), b.tt())
        print("time=1",d.timeIntervalSinceNow)
        
        
        d = Date()
        await a.tt()
        await b.tt()
        print("time2=",d.timeIntervalSinceNow)
        
        d = Date()
        async let q = (a.tt(), b.tt())
        await q
        print("time3=",d.timeIntervalSinceNow)
        
        d = Date()
        async let q1 = a.tt()
        async let q2 = b.tt()
        await q1
        await q2
        print("time4=",d.timeIntervalSinceNow)
        
        d = Date()
        async let q3 = a.tt()
        async let q4 = b.tt()
        await (q3, q4)
        print("time5=",d.timeIntervalSinceNow)
    }
}

打印输出:

time1= -0.4335060119628906
time2= -0.435217022895813
time3= -0.4430699348449707
time4= -0.23430800437927246
time5= -0.23900198936462402

【讨论】:

    【解决方案2】:

    简答:

    如果您想要并发执行,请使用async let 模式或任务组。


    长答案:

    你说:

    但是下面的语句会并行运行 do 和 stuff 正确吗?

    await (do(), stuff()) 
    

    不,他们不会。

    这是最好的经验说明:

    • 使任务花费足够的时间,以便容易表现出并发行为;和
    • 在 Instruments 中使用“Points of Interest”工具(例如,通过选择“Time Profiler”模板)以图形方式表示随时间变化的间隔。

    考虑这段代码,使用元组方法:

    import os.log
    
    private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: .pointsOfInterest)
    

    还有:

    func example() async {
        let values = await (self.doSomething(), self.doSomethingElse())
        print(values)
    }
    
    func doSomething() async -> Int {
        spin(#function)
        return 1
    }
    
    func doSomethingElse() async -> Int {
        spin(#function)
        return 42
    }
    
    func spin(_ name: StaticString) {
        let id = OSSignpostID(log: log)
        os_signpost(.begin, log: log, name: name, signpostID: id, "begin")
    
        let start = CACurrentMediaTime()
        while CACurrentMediaTime() - start < 1 { }   // spin for one second
    
        os_signpost(.end, log: log, name: name, signpostID: id, "end")
    }
    

    这导致图表显示它没有同时发生:

    鉴于:

    func example() async {
        async let foo = self.doSomething()
        async let bar = self.doSomethingElse()
        let values = await (foo, bar)
        print(values)
    }
    

    这确实会导致并发执行:


    现在,在上面的示例中,我更改了函数以便它们返回值(因为这确实是使用元组具有实际意义的唯一上下文)。

    但如果它们不返回值并且您希望它们并行运行,则可以使用任务组:

    func experiment() async {
        await withTaskGroup(of: Void.self) { group in
            group.addTask { await self.doSomething() }
            group.addTask { await self.doSomethingElse() }
        }
    }
    
    func doSomething() async {
        spin(#function)
    }
    
    func doSomethingElse() async {
        spin(#function)
    }
    

    这会导致它们并行运行的相同图表。

    您也可以只创建 Task 实例,然后创建 await 它们:

    func experiment() async {
        async let task1 = Task { await doSomething() }
        async let task2 = Task { await doSomethingElse() }
        _ = await task1
        _ = await task2
    }
    

    但是当正在创建的任务数量在编译时可能未知时,任务组提供了更大的灵活性。

    【讨论】:

      猜你喜欢
      • 2016-09-18
      • 2013-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-19
      • 2014-06-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多