2-8.【Concurrency】在 TaskGroup 中,如果一个子任务抛出异常,其他任务会如何处理?

8 阅读2分钟

一句话结论(记住这个)

withThrowingTaskGroup 中,只要有一个子任务抛出错误:
1️⃣ 该错误会向外传播
2️⃣ 组内其余所有子任务都会被自动取消
3️⃣ 离开作用域前,Swift 会等待这些任务响应取消并结束


1️⃣ 先看最典型的例子

do {
    try await withThrowingTaskGroup(of: Int.self) { group in
        group.addTask {
            try await work1()
        }
        group.addTask {
            try await work2() // ❌ 这里抛错
        }
        group.addTask {
            try await work3()
        }

        for try await value in group {
            print(value)
        }
    }
} catch {
    print("caught:", error)
}

work2() 抛错时,发生的事是:


2️⃣ 运行期到底发生了什么?

✅ 1. 第一个错误被“锁定”

  • 第一个抛出的错误

    • 成为 TaskGroup 的失败原因
  • 后续错误:

    • 会被忽略(但仍会触发取消)

✅ 2. 其他子任务立刻收到取消信号

对其余任务来说:

Task.isCancelled == true

但要注意 ⚠️:

取消是协作式的(cooperative)

  • 如果任务:

    • 正在 await
    • 或显式检查 Task.checkCancellation()
  • 才会尽快退出


✅ 3. group 停止产出新结果

  • for try await value in group

    • 会立刻抛出错误
    • 不再 yield 剩余结果

✅ 4. 离开作用域前会“收尸”

即使抛错了:

withThrowingTaskGroup { ... }
  • 也会等待:

    • 所有子任务结束
    • 或因取消而终止

👉 不会有“漏网任务”。


3️⃣ 子任务能不能“无视”取消?

理论上可以,但强烈不推荐

group.addTask {
    while true {
        // 不 await,不检查取消
    }
}

结果:

  • TaskGroup 会被迫等它结束
  • 你等于“把结构化并发打穿了”

👉 正确做法:

group.addTask {
    try Task.checkCancellation()
    return try await work()
}

4️⃣ 非 throwing TaskGroup 会怎样?

withTaskGroup(of: Int.self) { group in
    group.addTask {
        throw MyError() // ❌ 编译错误
    }
}
  • 不允许抛错
  • 必须在子任务内部处理错误

例如:

group.addTask {
    do {
        return try await work()
    } catch {
        return -1
    }
}

5️⃣ 能不能只取消一部分任务?

不能。
这是 TaskGroup 的设计选择

TaskGroup 要么整体成功,要么整体失败。

如果你需要更细粒度控制:

  • 用多个 TaskGroup
  • 或手动管理 Task(不推荐)

6️⃣ 常见误区(非常常见)

❌ 误区 1:已经完成的任务会被“回滚”

不会。

  • 已完成任务:

    • 结果仍然存在
  • 只是你:

    • 拿不到它们了

❌ 误区 2:取消 = 立刻终止

不是。

  • Swift 的取消是:

    • 标记式
    • 协作式
  • 没有强杀线程


7️⃣ 官方设计背后的理由

Swift 选择这种行为是为了:

  1. 避免部分成功 + 部分失败的复杂语义
  2. 确保资源不会泄漏
  3. 保证结构化并发的可推理性

你永远知道:

“这个 group 要么给我所有结果,要么抛错。”


8️⃣ 面试级总结(一句话版)

withThrowingTaskGroup 中,一旦任一子任务抛出异常,其它子任务会被自动取消;错误立即向外传播,TaskGroup 在退出作用域前会等待所有子任务结束或响应取消,从而保证结构化并发的完整性和资源安全。