SwiftUI — TaskGroup

🚀 Swift 并发中的 withTaskGroupTaskGroup 全面解析 #

Swift 5.5 引入了结构化并发(Structured Concurrency),其中 withTaskGroupTaskGroup 是实现并发任务管理的核心工具。它们可以让你轻松创建、管理和取消多个异步任务,极大地提升代码的性能与可读性。

https://developer.apple.com/documentation/swift/taskgroup


🧩 1. 什么是 withTaskGroupTaskGroup #

  • withTaskGroup:是一个函数,用于创建和管理一组并发任务。它提供了一个作用域,确保所有任务在退出该作用域前都已完成。
  • TaskGroup:是任务组的具体类型,表示一组异步任务。它会自动管理任务的生命周期和结果收集。

2. withTaskGroup 的基本用法 #

📄 示例:并发执行多个任务 #

import Foundation

func fetchNumbers() async -> [Int] {
    await withTaskGroup(of: Int.self) { group in
        // 添加任务到任务组
        for i in 1...5 {
            group.addTask {
                try? await Task.sleep(nanoseconds: UInt64(i) * 1_000_000_000)
                return i * 10  // 模拟异步计算
            }
        }

        // 收集任务结果
        var results = [Int]()
        for await result in group {
            results.append(result)
        }
        return results
    }
}

Task {
    let numbers = await fetchNumbers()
    print(numbers)  // 结果可能是 [10, 20, 30, 40, 50],但顺序不一定
}

解释: #

  • withTaskGroup(of: Int.self):创建一个任务组,处理 Int 类型的结果。
  • group.addTask {}:向任务组中添加异步任务。
  • for await result in group:异步等待所有任务完成,按任务完成的顺序返回结果。

🚀 3. TaskGroup 的高级用法 #

🌟 (1) 并发处理多个异步网络请求 #

import Foundation

func fetchURLs() async -> [String] {
    let urls = [
        "https://jsonplaceholder.typicode.com/posts/1",
        "https://jsonplaceholder.typicode.com/posts/2",
        "https://jsonplaceholder.typicode.com/posts/3"
    ]

    return await withTaskGroup(of: String.self) { group in
        for url in urls {
            group.addTask {
                if let data = try? await URLSession.shared.data(from: URL(string: url)!).0,
                   let content = String(data: data, encoding: .utf8) {
                    return content
                }
                return "Error loading \(url)"
            }
        }

        var results = [String]()
        for await result in group {
            results.append(result)
        }
        return results
    }
}

Task {
    let data = await fetchURLs()
    print(data)
}

特点: #

  • 同时发起多个网络请求,极大提升性能。
  • 自动等待所有请求完成,收集结果。

🌈 (2) 动态添加任务(递归任务) #

func fibonacci(_ n: Int) async -> Int {
    if n <= 1 { return n }
    return await withTaskGroup(of: Int.self) { group in
        group.addTask { await fibonacci(n - 1) }
        group.addTask { await fibonacci(n - 2) }
        
        var result = 0
        for await value in group {
            result += value
        }
        return result
    }
}

Task {
    let result = await fibonacci(6)
    print(result)  // 输出 8
}

亮点: #

  • 使用递归异步计算 Fibonacci 数列。
  • TaskGroup 自动管理任务的并发和结果合并。

🔒 4. 任务取消(Cancellation) #

TaskGroup 支持任务取消,能够在某个任务失败或不再需要时中止其他任务,避免资源浪费。

示例:任务取消 #

func fastFailingTasks() async -> Int? {
    await withTaskGroup(of: Int?.self) { group in
        group.addTask {
            try? await Task.sleep(nanoseconds: 2_000_000_000)
            return 42
        }
        
        group.addTask {
            return nil // 模拟快速失败
        }
        
        for await result in group {
            if result == nil {
                group.cancelAll()  // 取消所有剩余任务
                return nil
            }
        }
        return nil
    }
}

Task {
    let result = await fastFailingTasks()
    print(result ?? "Cancelled early")
}

关键点: #

  • group.cancelAll():取消所有仍在运行的任务。
  • 被取消的任务会尽早停止,释放系统资源。

🧪 5. throwingTaskGroup:处理异步错误 #

如果任务组中的任务可能抛出错误,可以使用 withThrowingTaskGroup

示例:捕获异步错误 #

func fetchDataWithError() async throws -> [String] {
    try await withThrowingTaskGroup(of: String.self) { group in
        group.addTask {
            throw URLError(.badServerResponse)
        }
        
        group.addTask {
            return "Data Loaded Successfully"
        }
        
        var results = [String]()
        for try await result in group {
            results.append(result)
        }
        return results
    }
}

Task {
    do {
        let data = try await fetchDataWithError()
        print(data)
    } catch {
        print("Error: \(error.localizedDescription)")
    }
}

亮点: #

  • withThrowingTaskGroup:支持抛出错误的异步任务组。
  • for try await:在遍历时捕获错误。

📊 6. withTaskGroup vs async let 的区别 #

特性withTaskGroupasync let
并发任务数量支持动态数量的任务固定数量,适合静态任务
错误处理支持 withThrowingTaskGroup使用 try await 处理错误
取消任务可手动调用 group.cancelAll() 取消所有任务不能取消单个任务,只能取消整个 Task
任务结果收集使用 for await 异步遍历结果直接 await 等待每个任务完成
适用场景适合大量动态任务(如批量网络请求)适合少量固定任务(如简单并发计算)

🔑 7. 最佳实践 #

  1. 任务粒度合理:避免将大量小任务放入 TaskGroup,这会增加调度开销。适合处理耗时任务。
  2. 及时取消无用任务:使用 group.cancelAll() 可以节省资源,特别是在某个任务失败时。
  3. 错误传播机制:优先使用 withThrowingTaskGroup 处理可能抛出错误的异步任务,保证代码健壮性。
  4. 主线程安全更新:在 SwiftUI 中,确保 UI 更新操作加上 @MainActor

🎯 总结 #

  • withTaskGroupTaskGroup 是 Swift 并发的核心工具,帮助你高效管理并发任务,简化异步代码。
  • 支持动态任务、错误处理、任务取消等高级特性,极大提升代码性能与健壮性。
  • 在复杂的异步场景(如批量网络请求、大规模计算等)中,TaskGroup 是你不可或缺的利器。
本文共 1509 字,创建于 Feb 5, 2025
相关标签: SwiftUI