Swift Concurrency 是 Swift 5.5 及以后版本引入的一套现代并发编程模型,旨在简化异步代码的编写,提高代码的可读性、可维护性和安全性。它提供了 async/awaitTaskActor 等核心概念,让开发者能够以更直观的方式处理并发任务。


一、入门:基础概念与语法

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 模型和并发安全。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐