Swift 5.5,何时在自定义异步实现中使用 `Task.suspend`?

标签 swift async-await swift5

新的 Async/Await 语法看起来很棒!但我想知道如何实现我自己的异步实现。

我偶然发现了这个 API:

此 API 允许我随时手动暂停任务。问题是,我不确定应该如何做,才能从并发中受益而不是避免不良做法。

也就是说,我不知道Task.suspend()的最佳实践

例如:

func example() async {
    for i in 0..<100 {
        print("example", i)
        await Task.suspend() // <-- is this OK?
    }
}

一些具体问题:

  • 应该多久调用一次挂起
  • 应该在密集操作之前还是之后调用挂起? (例如:IO、加密等...)
  • 挂起的调用是否应该有最大数量?
  • 密集调用 suspend 的“代价”是多少?
  • 什么时候不应该调用挂起
  • 还有其他方法可以实现这种并发(异步/等待风格,而不是 GCD)

现实生活中的例子,我正在实现一个加密大文件内容的函数,因为它是一个IO+加密密集型任务,所以它应该是异步的,我想知道如何使用Task.suspend(或任何其他异步/等待工具)以使其异步。

最佳答案

调用 Task.suspend() 会将当前任务挂起几毫秒,以便为任何可能等待的任务留出一些时间,如果您正在进行密集工作,这一点尤其重要在循环中,所有任务都使用相同的优先级。否则,您的繁重任务可能会停止应用程序中的所有异步代码。例如:

func f() async {
    for _ in 0...10 {
        var arr = (1...10000).map {_ in arc4random()}
        arr.sort()
    }
    print("f")
}

func z() async {
    print("z")
}

// Run in parallel

Task {
    await f()
}

Task {
    await z()
}

输出:

f
z

如您所见,z() 等待 f(),因为它多次执行长时间运行的大型数组排序操作。要解决此问题,您可以在循环中添加 Task.suspend():

func f() async {
    for _ in 0...10 {
        var arr = (1...10000).map {_ in arc4random()}
        arr.sort()

        await Task.suspend() // Voluntarily suspend itself 
    }
    print("f")
}

输出:

z
f

async/await 在其自己的协作并发队列上工作,如果您不想暂停,请考虑将任务移至非默认优先级(队列),例如任务(优先级:.background)或在单独的队列上运行繁重的任务。

关于Swift 5.5,何时在自定义异步实现中使用 `Task.suspend`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68646769/

相关文章:

c# - Xamarin 表单 - 异步 ContentPage

Swift 正确计算时间占一天的比例

ios - AVAudioPlayer 导致应用程序崩溃

c# - ValueTask 有 ContinueWith 吗?

c# - 出错时重试异步文件上传

ios - Swift 5 应用程序是否只能在特定的 iOS 版本上运行?

html - iOS 13 Swift 5 wkwebview - 将文档目录中的图像显示到 wkwebview 真实设备中

swift - 如何使用 CNContactStore 从 CNContainer 获取 CNGroup 数组?

ios - 使用 tableview 时 xcode 中的可用重载

ios - 从嵌入的 tableviewcontroller 中执行Segue时如何保留NavigationController