【问题标题】:Async await for closure -> spinlock?异步等待关闭 -> 自旋锁?
【发布时间】:2022-08-19 12:36:49
【问题描述】:

我正在维护一个将使用 SwiftUI 的遗留代码库。我正在使用新的可刷新 api:https://developer.apple.com/documentation/SwiftUI/View/refreshable(action:)。他们强迫你使用异步等待有点烦人,因为我有一部分刷新代码不能完全异步等待。

我不能对此代码使用异步等待的原因是应用程序通过网络套接字.应用程序手动匹配对请求的响应,然后在它们到达时调用闭包。我不相信完全重写为异步等待是可能的。

我有一个最终将始终返回结果的闭包。当结果到来时,微调器应该消失。这是我可怕的异步等待包装器(简化):

/// Terrible way of adding async await support
func sendAsync() async {
    try! await Task {
        var future: Void? = nil

        self.onReceive = { _ in
            future = ()
        }

        while true {
            if future != nil {
                return
            }

            // This is 10 ms
            try await Task.sleep(nanoseconds: 10_000_000)
        }
    }.value
}

所以这是一个基于自旋锁的异步等待包装器。我想知道是否有更好的方法来做到这一点。我包裹在Task 中,它不会以这种方式在主线程上运行。

是否有一些现有的异步等待包装器等待值非零然后唤醒? refreshable 是否有不同的 api不是需要异步等待?

  • \“应用程序手动匹配对请求的响应,然后在它们到达时调用闭包。我认为这不可能完全重写为异步等待。\”也许这应该是你的问题?听起来像我可能,使用通常的不安全的延续包装。
  • @matt 我不知道继续,但我目前正在使用 withCheckedContinuation,这太棒了!我知道有一个真正让它异步等待的起点,谢谢你的提示
  • 是的,withCheckedContinuation。或者,如果你愿意,这闻起来有点像AsyncSequence(在这种情况下你会使用AsyncStream)。
  • WWDC 2021 视频Meet AsyncSequence
  • 除了您应该在发货前更改为withUncheckedContinuation

标签: swift swiftui


【解决方案1】:

正如 cmets 中已经建议的那样,如果您只收到一次数据,您可以使用 continuation

func sendAsync() async {
    await withCheckedContinuation { continuation in
        self.onReceive = { _ in
            continuation.resume()
        }
    }
}

或接收数据序列使用AsyncStream

func sendAsync() -> AsyncStream<T> {
    return .init { continuation in
        self.onReceive = { value in
            continuation.yield(value)
        }
    }
}

Apple 警告使用 continuations 并多次恢复它们,因此在使用它们时要格外小心:

多次从延续中恢复是未定义的行为。永不恢复会使任务无限期地处于挂起状态,并泄漏任何相关资源。如果违反了这些不变量中的任何一个,CheckedContinuation 会记录一条消息。

【讨论】:

    猜你喜欢
    • 2016-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-11
    • 2021-12-20
    • 2014-11-23
    • 2020-01-03
    • 2015-12-04
    相关资源
    最近更新 更多