Swift async let 取消不起作用

Swift async let cancellation doesn't work

我对被取消的任务有点困惑。

概览:

疑问:

注:

输出:

A - started
B - going to throw
A - going to return, Task.isCancelled = false
error: infinity

并发函数定义:

import Foundation
import UIKit

enum ComputationError: Error {
    case infinity
}

fileprivate func computeA() async throws -> Int {
    print("A - started")
    await Task.sleep(2 * 100_000_000)
    print("A - going to return, Task.isCancelled = \(Task.isCancelled)") //I expected Task.isCancelled to be true
    return 25
}

fileprivate func computeB() async throws -> Int {
    print("B - going to throw")
    throw ComputationError.infinity
}

func checkCancellation() async throws {
    async let a = computeA()
    async let b = computeB()
    
    let c = try await a + b
    print("c = \(c)")
}

调用并发函数

struct ContentView: View {
    var body: some View {
        Button("check cancellation") {
            Task {
                do {
                    try await checkCancellation()
                    print("normal exit")
                } catch {
                    print("error: \(error)")
                }
            }
        }
    }
}

观察:

输出:

A - started
B - going to throw
A - going to return, Task.isCancelled = true
error: infinity

疑问:

我仍然不确定我是否理解原始代码中出现这种行为的原因

I expected child task computeA to be cancelled because computeB threw an error, but computeA was never cancelled. Is my understanding wrong or am I missing something?

是的,是的。你的理解有误,你漏掉了什么。

视频中的评论与在任务组中创建的子任务有关。实际发生的情况是,当错误从 child 任务渗透到任务组时,任务组会在所有其他 child 任务上调用 cancel

但是正在使用async let进行测试。没有任务组。所以没有人“在那里”取消另一个子任务。事实上,如果你仔细观看视频,你会发现它正确地告诉了你预期的内容:

  • 如果取消parent个任务,child个任务也会自动取消。

  • 如果 async let 任务在第二个 async let 任务甚至初始化之前抛出,则第二个任务“出生取消”。 还在等待(这个视频说的很清楚,没错);如果它没有通过中止响应取消,它将继续完成。

这里的重点是parent任务必须有条不紊地结束。在它的 children 以某种方式完成之前,它不会结束。 (同样,视频对此非常清楚。)如果这意味着自动等待任务完成而不管抛出,那么就会发生这种情况。

另一个需要注意的有趣的事情是,使用 async let,你稍后说 try await,如果子任务抛出,你甚至不会听到它(并且什么都不会发生这方面),直到你到达 try await 发生的地步。换句话说,throw 只能渗透到你实际说 try 的地方;没有其他意义。