Swift Concurrency从入门到精通
·
Swift Concurrency 是 Swift 5.5 及以后版本引入的一套现代并发编程模型,旨在简化异步代码的编写,提高代码的可读性、可维护性和安全性。它提供了 async/await、Task、Actor 等核心概念,让开发者能够以更直观的方式处理并发任务。
一、入门:基础概念与语法
1. 为什么需要 Swift Concurrency?
- 传统异步编程的痛点:
- 回调地狱(Callback Hell):嵌套层级深,代码难以阅读和维护。
- 线程管理复杂:手动管理
DispatchQueue容易出错。 - 错误处理繁琐:闭包中传递错误不够直观。
- Swift Concurrency 的优势:
- 用同步的语法编写异步代码,可读性更高。
- 内置线程管理,无需手动操作
DispatchQueue。 - 原生支持错误处理,与
try/catch无缝集成。
2. 核心关键字与概念
(1)async / await
async:标记一个函数为异步函数,它可能会挂起(suspend)并在之后恢复。await:用于调用异步函数,等待其完成并获取结果。
示例:
swift
// 异步函数:模拟网络请求
func fetchData() async -> String {
await Task.sleep(1_000_000_000) // 等待 1 秒(单位:纳秒)
return "Hello, Swift Concurrency!"
}
// 调用异步函数
async {
let result = await fetchData()
print(result) // 输出:Hello, Swift Concurrency!
}
(2)Task
Task代表一个并发任务,可以在后台或前台执行。- 可以通过
Task启动一个新的并发任务。
示例:
swift
// 启动一个任务
Task {
print("Task started")
await Task.sleep(500_000_000) // 等待 0.5 秒
print("Task finished")
}
(3)TaskGroup
TaskGroup用于管理一组相关的任务,可以等待所有任务完成,或取消整个组。
示例:
swift
// 使用 TaskGroup 并发执行多个任务
async {
await withTaskGroup(of: Int.self) { group in
for i in 1...5 {
group.addTask {
await Task.sleep(UInt64(i) * 100_000_000) // 每个任务等待 i * 0.1 秒
return i * 2
}
}
// 收集所有任务的结果
var results = [Int]()
for await result in group {
results.append(result)
}
print(results) // 输出:[2, 4, 6, 8, 10](顺序不确定)
}
}
二、进阶:任务管理与错误处理
1. 任务优先级
TaskPriority定义了任务的优先级,系统会根据优先级调度任务。- 常见优先级:
.high、.medium(默认)、.low、.background。
示例:
swift
Task(priority: .high) {
print("High priority task")
}
Task(priority: .low) {
print("Low priority task")
}
2. 任务取消
- Swift Concurrency 支持协作式取消,任务可以通过
Task.isCancelled检查是否被取消,并自行处理。
示例:
swift
func longRunningTask() async throws -> Int {
for i in 1...10 {
if Task.isCancelled {
throw CancellationError() // 抛出取消错误
}
await Task.sleep(100_000_000) // 等待 0.1 秒
}
return 10
}
// 启动任务并取消
async {
let task = Task { try await longRunningTask() }
await Task.sleep(500_000_000) // 等待 0.5 秒后取消
task.cancel()
do {
let result = try await task.value
print("Result: \(result)")
} catch is CancellationError {
print("Task cancelled")
}
}
3. 错误处理
- 异步函数可以抛出错误,使用
try await调用,并通过try/catch捕获。
示例:
swift
enum NetworkError: Error {
case invalidURL
case requestFailed
}
func fetchData() async throws -> String {
guard let url = URL(string: "https://example.com") else {
throw NetworkError.invalidURL
}
let (data, _) = try await URLSession.shared.data(from: url)
return String(data: data, encoding: .utf8)!
}
// 调用并处理错误
async {
do {
let result = try await fetchData()
print(result)
} catch NetworkError.invalidURL {
print("Invalid URL")
} catch NetworkError.requestFailed {
print("Request failed")
} catch {
print("Unknown error: \(error)")
}
}
三、精通:Actor 模型与并发安全
1. Actor 模型
Actor是 Swift Concurrency 中用于保证并发安全的核心机制。Actor内部的状态只能通过其自身的方法访问,避免了多线程竞争。
示例:
swift
actor Counter {
private var count = 0
func increment() {
count += 1
}
func getCount() -> Int {
count
}
}
// 使用 Actor
async {
let counter = Counter()
await counter.increment() // 必须使用 await 调用 Actor 的方法
let count = await counter.getCount()
print(count) // 输出:1
}
2. @MainActor
@MainActor用于标记一个类型或方法必须在主线程上执行,适用于 UI 更新等场景。
示例:
swift
@MainActor
class ViewModel {
var message: String = ""
func updateMessage() {
message = "Updated on main thread"
}
}
// 调用
async {
let viewModel = ViewModel()
await viewModel.updateMessage() // 自动切换到主线程执行
}
3. nonisolated 方法
nonisolated标记一个 Actor 的方法不访问其内部状态,因此可以在非隔离上下文中调用。
示例:
swift
actor Counter {
private var count = 0
func increment() {
count += 1
}
nonisolated func getInfo() -> String {
"This is a counter" // 不访问内部状态
}
}
// 调用 nonisolated 方法不需要 await
let counter = Counter()
print(counter.getInfo()) // 输出:This is a counter
四、实战:常见场景应用
1. 并发网络请求
swift
async {
async let data1 = fetchData(from: "https://example.com/data1")
async let data2 = fetchData(from: "https://example.com/data2")
async let data3 = fetchData(from: "https://example.com/data3")
// 并发等待所有请求完成
let results = await [data1, data2, data3]
print(results)
}
2. 后台任务与 UI 更新
swift
@MainActor
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData() {
Task {
let data = await fetchData() // 后台执行网络请求
updateUI(with: data) // @MainActor 保证在主线程更新 UI
}
}
func updateUI(with data: String) {
label.text = data
}
}
五、总结与学习资源
1. 学习路径
- 入门:掌握
async/await和Task的基本用法。 - 进阶:学习任务管理、错误处理和
TaskGroup。 - 精通:深入理解
Actor模型和并发安全。
更多推荐



所有评论(0)