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()
}
Back to Blog

相关文章

阅读更多 »

SC #11:任务组

TaskGroup 一个 TaskGroup 包含动态创建的子任务,可以串行或并发执行。只有当组完成时才被视为结束。

SC #8:取消 Task

Swift 和 SwiftUI 中的 Task 取消 > 注意:在 Swift 中,取消 Task 并不保证执行会立即停止。每个 Task 必须检查更多…