🚀 Swift 并发中的 withTaskGroup
与 TaskGroup
全面解析
#
Swift 5.5 引入了结构化并发(Structured Concurrency),其中 withTaskGroup
和 TaskGroup
是实现并发任务管理的核心工具。它们可以让你轻松创建、管理和取消多个异步任务,极大地提升代码的性能与可读性。
https://developer.apple.com/documentation/swift/taskgroup
🧩 1. 什么是 withTaskGroup
和 TaskGroup
?
#
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
的区别
#
特性 | withTaskGroup | async let |
---|---|---|
并发任务数量 | 支持动态数量的任务 | 固定数量,适合静态任务 |
错误处理 | 支持 withThrowingTaskGroup | 使用 try await 处理错误 |
取消任务 | 可手动调用 group.cancelAll() 取消所有任务 | 不能取消单个任务,只能取消整个 Task |
任务结果收集 | 使用 for await 异步遍历结果 | 直接 await 等待每个任务完成 |
适用场景 | 适合大量动态任务(如批量网络请求) | 适合少量固定任务(如简单并发计算) |
🔑 7. 最佳实践 #
- 任务粒度合理:避免将大量小任务放入
TaskGroup
,这会增加调度开销。适合处理耗时任务。 - 及时取消无用任务:使用
group.cancelAll()
可以节省资源,特别是在某个任务失败时。 - 错误传播机制:优先使用
withThrowingTaskGroup
处理可能抛出错误的异步任务,保证代码健壮性。 - 主线程安全更新:在 SwiftUI 中,确保 UI 更新操作加上
@MainActor
。
🎯 总结 #
withTaskGroup
和TaskGroup
是 Swift 并发的核心工具,帮助你高效管理并发任务,简化异步代码。- 支持动态任务、错误处理、任务取消等高级特性,极大提升代码性能与健壮性。
- 在复杂的异步场景(如批量网络请求、大规模计算等)中,
TaskGroup
是你不可或缺的利器。