SC #11:任务组
I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have it, I’ll keep the source link at the top and translate the rest into Simplified Chinese while preserving all formatting, markdown, and code blocks.
TaskGroup
TaskGroup 包含动态创建的子任务,可以串行或并发执行。只有当 所有 子任务都完成时,组才被视为已结束。
withTaskGroup(of:returning:isolation:body:) 允许创建上下文,以使用 addTask(priority:operation:) 添加子任务。
基本示例:下载图片
func downloadPhoto(url: URL) async -> UIImage {
// …
}
await withTaskGroup(of: UIImage.self) { taskGroup in
let photoURLs = await listPhotoURLs(inGallery: "Vacaciones")
for photoURL in photoURLs {
taskGroup.addTask {
await downloadPhoto(url: photoURL)
}
}
}
TaskGroup 的行为类似于 forEach,并发执行每个子任务并收集结果。
收集结果
作为集合
可以将返回类型定义为集合(例如 [UIImage].self)。在启动所有子任务后,使用组的 AsyncSequence 来等待结果并构建最终的集合。
let images: [UIImage] = await withTaskGroup(
of: UIImage.self,
returning: [UIImage].self
) { taskGroup in
let photoURLs = await listPhotoURLs(inGallery: "Vacaciones")
for photoURL in photoURLs {
taskGroup.addTask {
await downloadPhoto(url: photoURL)
}
}
var images = [UIImage]()
for await result in taskGroup {
images.append(result)
}
return images
}
使用 reduce
由于 TaskGroup 符合 AsyncSequence,也可以使用 reduce 运算符:
await withTaskGroup(of: UIImage.self, returning: [UIImage].self) { taskGroup in
let photoURLs = try! await listPhotoURLs(inGallery: "Vacaciones")
for photoURL in photoURLs {
taskGroup.addTask {
try! await downloadPhoto(url: photoURL)
}
}
return await taskGroup.reduce(into: [UIImage]()) { partialResult, image in
partialResult.append(image)
}
}
Source: …
ThrowingTaskGroup
当某个子任务可能抛出错误时,使用 withThrowingTaskGroup(of:returning:isolation:body:)。
try await withThrowingTaskGroup(of: UIImage.self, returning: [UIImage].self) { taskGroup in
let photoURLs = try await listPhotoURLs(inGallery: "Vacaciones")
for photoURL in photoURLs {
taskGroup.addTask {
try await downloadPhoto(url: photoURL)
}
}
return try await taskGroup.reduce(into: [UIImage]()) { partialResult, image in
partialResult.append(image)
}
}
错误行为
ThrowingTaskGroup不会在子任务抛出异常时自动失败;子任务会被标记为失败,但组会继续执行。- 要传播错误并取消剩余任务,需要使用
group.next()显式地获取每个子任务的结果。
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask { throw SomeError() }
// 这里组不会失败
}
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask { throw SomeError() }
try await group.next() // 传播错误并取消正在进行的任务
}
next()
group.next() 逐个返回子任务的结果,顺序是随机的,如果子任务失败则抛出相应的错误。这使得可以单独处理每个错误。
while let result = try await taskGroup.next() {
print("Got a new result:", result)
}
任务取消
- 可以使用
group.cancelAll()取消整个任务组。 - 如果通过
addTask(priority:operation:)向已取消的组添加任务,该任务会在创建后立即被取消。若想防止任务甚至开始执行,请使用addTaskUnlessCancelled(priority:operation:)。
for photoURL in photoURLs {
let didAddTask = taskGroup.addTaskUnlessCancelled {
try await downloadPhoto(url: photoURL)
}
print("Added task: \(didAddTask)")
}