SC #10:解耦任务
发布: (2026年1月20日 GMT+8 05:18)
3 min read
原文: Dev.to
Source: Dev.to
一个脱离上下文的任务(Detached Task)会以异步方式执行操作,脱离包裹它的结构化并发上下文。未继承该上下文意味着不会继承:
- 上下文的优先级
- 上下文的取消状态。
创建任务使用静态方法 Task.detached(name:priority:operation:):
Task.detached {
// 在结构化并发上下文之外以异步方式执行操作
}
基本示例
func detachedTaskExample() async {
func asyncFunc(_ string: String) async {
print(string)
}
await asyncFunc("\(1)")
Task.detached {
await asyncFunc("\(2)")
}
await asyncFunc("\(3)")
}
上述代码可能产生以下结果,顺序并不保证:
1
3
2
脱离任务的取消风险
考虑下面的示例,其中 longRunningAsyncOperation 是一个会耗时的异步任务(在被取消之前不会结束),detachedTaskExample 是一个调用 longRunningAsyncOperation 作为子任务的任务,然后创建一个 Task.detached 来脱离调用它:
func detachedTaskExample() async {
let outerTask = Task {
// 这个子任务会被取消
await longRunningAsyncOperation(1)
// 这个脱离任务不继承取消状态
Task.detached(priority: .background) {
// 所以这里的 checkCancellation 不会起作用
try Task.checkCancellation()
// 所以这个子任务也不会被取消
await longRunningAsyncOperation(2)
}
}
outerTask.cancel()
}
private func longRunningAsyncOperation(_ id: Int) async {
do {
print("Empezando tarea \(id)")
try await Task.sleep(for: .seconds(5))
print("Terminé la tarea")
} catch {
print("\(#function) - Tarea \(id) falló con error: \(error)")
}
}
该示例的输出为:
Empezando tarea 1
longRunningAsyncOperation(_:) - Tarea 1 falló con error: CancellationError()
Empezando tarea 2
Terminé la tarea
在此示例中:
- 第一次调用
longRunningAsyncOperation能够正确被取消,因为它在已取消的Task的结构化上下文中被调用。 - 第二次调用时,虽然执行了
try Task.checkCancellation(),但由于 没有继承取消状态,因此无法检测到取消。
要取消一个脱离的 Task,必须保留对它的引用并手动调用 cancel()。这类任务在引用被释放时不会自动取消,需要显式管理。
何时使用脱离任务?
在以下场景中创建脱离任务是合适的:
- 操作可以独立执行。
- 不需要与容器上下文保持关联。
- 即使容器已被取消,也可以接受任务继续运行。
有效示例(没有对 self 的引用):
Task.detached(priority: .background) {
await DirectoryCleaner.cleanup()
}