Swift 中的并发与多线程:高级水平
\n\n现代 iOS 和 macOS 应用程序需要高性能和响应式界面。Swift 中的高级并发处理远不止简单使用 Grand Central Dispatch (GCD)。在本文中,我们将探讨 Swift 5.5+ 中用于管理异步任务的关键工具:async/await、Actors、TaskGroup 和 Sendable 协议。您将学习如何避免数据竞争、正确构建多线程代码以及优化性能。
1. async/await 与结构化并发基础
\n\n关键字 async 和 await 是现代 Swift 并发的基础。它们允许编写看起来像同步代码的异步代码,从而大大简化了阅读和调试。
声明异步函数:
\nfunc fetchUserData() async throws -> User {\n let url = URL(string: "https://api.example.com/user")!\n let (data, _) = try await URLSession.shared.data(from: url)\n let decoder = JSONDecoder()\n return try decoder.decode(User.self, from: data)\n}\n\n调用异步函数并处理错误:
\nTask {\n do {\n let user = try await fetchUserData()\n print("User name: \\(user.name)")\n } catch {\n print("Failed to fetch user: \\(error)")\n }\n}\n\n重要事项:
\n- \n
- 结构化并发: 每个异步任务都有明确的生存期,受父级
Task作用域的限制。 \n - 任务取消: 当父级 Task 被取消时,子任务会自动取消。 \n
- 优先级: 对于用户重要的任务,使用
Task(priority: .userInitiated)。 \n
2. Actors:防止数据竞争
\n\n在 Swift 5.5 之前,开发者经常使用 GCD 的串行队列来保护共享状态。Actors 提供了内置的隔离机制:只能通过 await 访问 actor 的状态,这保证了编译器级别的线程安全。
银行账户的 Actor 示例:
\nactor BankAccount {\n private var balance: Double = 0.0\n\n func deposit(amount: Double) {\n balance += amount\n }\n\n func withdraw(amount: Double) throws {\n guard balance >= amount else {\n throw BankError.insufficientFunds\n }\n balance -= amount\n }\n\n func getBalance() -> Double {\n return balance\n }\n}\n\n// 使用\nlet account = BankAccount()\nTask {\n await account.deposit(amount: 100.0)\n let currentBalance = await account.getBalance()\n print("Balance: \\(currentBalance)")\n}\n\nActors 的优势:
\n- \n
- 编译器检查: 尝试在没有
await的情况下访问 actor 的属性会导致编译错误。 \n - 重入性: Actor 可以暂停当前方法的执行(例如,在 actor 内部使用
await时)并处理另一个调用,从而防止死锁。 \n - 性能: Actors 使用优化的同步机制,通常比手动锁更快。 \n
3. TaskGroup:并行数据处理
\n\n当需要执行多个独立的异步操作并等待它们全部完成时,TaskGroup 是理想的工具。它会自动管理并行任务的数量并收集结果。
并行下载多个图像的示例:
\nfunc downloadImages(urls: [URL])