897 字
4 分钟
Swift Task Group
2025-03-12

Task#

  • 定义
    Task 是 Swift 结构化并发模型中的基本工作单元,代表一个异步操作的执行。所有异步代码(如 async 函数)都运行在某个 Task 中。

  • 特点

    • 层级结构:任务可以形成父子关系(父任务与子任务),构成任务树。
    • 隔离性:任务之间默认相互隔离,但可通过 actor 安全共享状态。
    • 取消机制:支持协作式取消(通过 Task.isCancelledTask.checkCancellation() 检查)。
    • 优先级:可指定优先级(如 .high, .low),子任务默认继承父任务的优先级。
  • 创建方式

    • 隐式创建:使用 async let 创建子任务(自动绑定到当前任务)。
      async let photo1 = downloadPhoto(named: "photo1")
      let photos = await [photo1, photo2, photo3]
    • 显式创建
      • 结构化任务:通过 TaskGroup 添加子任务。
      • 非结构化任务:使用 Task.init(继承当前上下文)或 Task.detached(独立上下文)。
        let task = Task { await someAsyncWork() }
        let result = await task.value
  • 关键行为

    • 任务挂起时(遇到 await),线程可能被其他任务复用。
    • 父任务取消时,所有子任务自动取消。
    • 任务结果通过 await 获取,错误通过 try 传递。

TaskGroup#

  • 定义
    TaskGroup 用于显式管理一组子任务,支持动态添加任务并控制并发行为,是结构化并发的核心工具。

  • 用途

    • 并行执行多个独立任务(如批量下载资源)。
    • 收集子任务结果或处理完成顺序。
  • 使用步骤

    1. 创建任务组:通过 withTaskGroupwithThrowingTaskGroup(支持错误传递)。
    2. 添加子任务:使用 group.addTask
    3. 等待结果:通过 for await 遍历结果或直接收集。
  • 示例

    let photos = await withTaskGroup(of: Optional<Data>.self) { group in
        let photoNames = await listPhotos(inGallery: "Summer Vacation")
        for name in photoNames {
            let added = group.addTaskUnlessCancelled {
                guard !Task.isCancelled else { return nil }
                return await downloadPhoto(named: name)
            }
            guard added else { break }
        }
    
    
        var results: [Data] = []
        for await photo in group {
            if let photo { results.append(photo) }
        }
        return results
    }
  • 特点

    • 结构化生命周期:任务组确保所有子任务在闭包退出前完成。
    • 动态任务数量:可循环内动态添加任务。
    • 取消传播:取消任务组会取消所有子任务。
    • 结果顺序:子任务完成顺序不确定,但结果可按完成顺序收集。
  • async let 的区别

    async letTaskGroup
    任务数量固定(编译时确定)动态(运行时确定)
    结果收集需显式列出所有 async let通过循环遍历完成的任务
    适用场景少量已知并行任务批量或动态任务(如下载)

注意事项#

  1. 结构化并发
    通过任务父子关系确保:

    • 子任务在父任务退出前完成。
    • 错误和取消自动传播。
    • 避免资源泄漏(如未等待的任务)。
  2. 取消处理

    • 在任务中定期检查 Task.isCancelled 或调用 Task.checkCancellation()
    • 清理资源(如关闭网络连接)后退出。
  3. 线程安全

    • 跨任务共享数据需使用 Sendable 类型(如值类型、不可变类、actor)。
    • 避免在任务间直接共享可变引用类型。
  4. 性能权衡

    • 并行任务数量受系统资源限制(如 CPU 核心数)。
    • 过多任务可能导致线程竞争,需合理控制并发度。

总结#

  • Task 是并发的基本单元,用于封装异步操作,支持层级管理和取消。
  • TaskGroup 提供结构化方式管理动态子任务,适合批量并行工作。
  • 二者共同构建 Swift 的安全并发模型,通过编译时检查(如 Sendable)和运行时机制(如任务取消)避免常见并发问题。
Swift Task Group
https://blog.lpkt.cn/posts/swift-task-group/
作者
lollipopkit
发布于
2025-03-12
许可协议
CC BY-NC-SA 4.0