示例:批量下载图片并处理结果
Task Group的并行执行能力在批量处理任务时尤为强大,前两节介绍了其基本用法和并行原理。本节将通过一个完整的实战案例——批量下载图片并处理结果——展示如何在Swift中使用Task Group实现高效的异步流程。我们将设计一个图片下载模块,处理下载结果并更新UI,同时应对错误和性能需求,帮助你将结构化并发的知识转化为实际代码。
案例背景
假设我们要开发一个图片浏览应用,需要从多个URL下载图片,应用滤镜后显示在界面上。要求:
- 并行下载图片,缩短总耗时。
- 处理下载和滤镜应用的错误。
- 在主线程更新UI。
- 支持取消操作。
以下是逐步实现的过程。
实现步骤
1. 定义模型和错误类型
首先定义图片处理相关类型:
struct ProcessedImage {
let id: Int
let image: UIImage
}
enum ImageError: Error {
case downloadFailed(Error)
case invalidData
case processingFailed
}
ProcessedImage:包含ID和处理后的图片。ImageError:覆盖下载和处理中的异常。
2. 实现下载和处理函数
设计异步函数执行核心逻辑:
func downloadImage(from url: URL) async throws -> Data {
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
func processImage(data: Data, id: Int) async throws -> ProcessedImage {
guard let image = UIImage(data: data) else { throw ImageError.invalidData }
try? await Task.sleep(nanoseconds: 500_000_000) // 模拟滤镜处理
return ProcessedImage(id: id, image: image)
}
downloadImage:下载原始数据。processImage:将数据转为图片并模拟处理。
3. 使用Task Group批量处理
创建一个函数并行下载并处理图片:
func fetchAndProcessImages(from urls: [URL]) async throws -> [ProcessedImage] {
try await withThrowingTaskGroup(of: ProcessedImage.self) { group in
for (index, url) in urls.enumerated() {
group.addTask {
let data = try await downloadImage(from: url)
return try await processImage(data: data, id: index)
}
}
var results: [ProcessedImage] = []
for try await image in group {
results.append(image)
}
return results.sorted { $0.id < $1.id } // 按ID排序
}
}
withThrowingTaskGroup:支持错误抛出。addTask:并行执行下载和处理。- 结果按原始顺序排序。
4. 集成到视图控制器
将逻辑整合到UI更新中,支持取消:
@MainActor
class GalleryViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var statusLabel: UILabel!
private var images: [ProcessedImage] = []
private var downloadTask: Task<Void, Never>?
override func viewDidLoad() {
super.viewDidLoad()
startDownload()
}
private func startDownload() {
let urls = [
URL(string: "https://example.com/image1.jpg")!,
URL(string: "https://example.com/image2.jpg")!,
URL(string: "https://example.com/image3.jpg")!
]
downloadTask = Task {
statusLabel.text = "下载中..."
do {
images = try await fetchAndProcessImages(from: urls)
collectionView.reloadData()
statusLabel.text = "完成"
} catch ImageError.downloadFailed(let error) {
statusLabel.text = "下载失败:\(error.localizedDescription)"
} catch ImageError.invalidData {
statusLabel.text = "图片数据无效"
} catch {
statusLabel.text = "未知错误:\(error)"
}
}
}
@IBAction func cancelDownload(_ sender: UIButton) {
downloadTask?.cancel()
statusLabel.text = "已取消"
}
}
// UICollectionViewDataSource
extension GalleryViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath) as! ImageCollectionViewCell
cell.imageView.image = images[indexPath.item].image
return cell
}
}
@MainActor:确保UI操作在主线程。Task:封装整个流程,支持取消。- 错误分类型显示用户友好提示。
5. 添加取消检查
在任务中响应取消:
func fetchAndProcessImages(from urls: [URL]) async throws -> [ProcessedImage] {
try await withThrowingTaskGroup(of: ProcessedImage.self) { group in
for (index, url) in urls.enumerated() {
group.addTask {
try Task.checkCancellation() // 检查取消
let data = try await downloadImage(from: url)
try Task.checkCancellation() // 再次检查
return try await processImage(data: data, id: index)
}
}
var results: [ProcessedImage] = []
for try await image in group {
results.append(image)
}
return results.sorted { $0.id < $1.id }
}
}
checkCancellation():若任务取消,抛出CancellationError。
运行结果
- 成功时:三张图片并行下载和处理,约1.5秒后显示(下载+处理时间重叠)。
- 失败时:显示错误信息,如“下载失败:timeout”。
- 取消时:点击取消按钮,状态变为“已取消”,未完成任务停止。
分析与改进
优势
- 高效并行:总耗时接近最慢单任务时间。
- 错误集中:统一捕获并处理异常。
- 结构清晰:任务组管理生命周期,避免手动跟踪。
可改进之处
- 进度反馈:显示已完成任务数:
for try await image in group { results.append(image) await MainActor.run { statusLabel.text = "已完成\(results.count)/\(urls.count)" } } - 重试机制:对失败任务重试。
- 缓存支持:保存成功下载的图片。
测试建议
- 网络中断:关闭网络测试错误处理。
- 取消操作:下载中取消,确保任务停止。
- 性能评估:增加URL数量,观察并行效果。
小结
本节通过批量下载图片的案例,展示了Task Group在并行执行和结果处理中的强大能力。从任务设计到UI集成,再到取消支持,这一实践整合了结构化并发的核心概念。掌握此案例,你将能灵活应对多任务异步场景。本章回顾了结构化并发的优势,下一章将探讨非结构化并发,与之对比完善你的并发知识体系。
内容说明
- 结构:从背景到实现步骤,再到分析和总结。
- 代码:完整案例覆盖模型、下载、UI和取消,突出实用性。
- 语气:实践性且深入,适合技术书籍核心章节。
- 衔接:承接前两节(
Task Group基础和并行),预告后续(非结构化并发)。
