Swift 关于解决并发编程的新方式

3,572 阅读3分钟

Swift 关于解决并发编程的新方式

本文主要介绍使用async和await简化并发编程,使得异步操作更加直观和易读。

基本概念

  • async 标记一个函数是异步的,意味着它可能在执行过程中暂停以等待其他任务完成。

  • await 用于调用异步函数,表示在这个调用处暂停,直到该异步操作完成并返回结果。

定义异步函数

异步函数通过在函数声明中使用 async 关键字来定义:

func fetchData() async -> String {
    // 模拟异步操作,如网络请求
    await Task.sleep(2 * 1_000_000_000) // 等待2秒
    return "数据已获取"
}

调用异步函数

调用异步函数需要在 await 前加上 async,通常这些调用必须在异步上下文中进行。

func displayData() async {
    let data = await fetchData()
    print(data)
}

在异步上下文中调用

为了在常规代码中调用异步函数,你需要在异步上下文中进行,比如在 Task 内部或其他异步函数中:

Task {
    await displayData()
}

错误处理

异步函数可以抛出错误(使用 throws),在调用时也需要处理这些错误:

func fetchData() async throws -> String {
    // 模拟可能抛出错误的异步操作
    if Bool.random() {
        throw NSError(domain: "", code: 1, userInfo: nil)
    }
    return "数据已获取"
}

func displayData() async {
    do {
        let data = try await fetchData()
        print(data)
    } catch {
        print("获取数据时出错: \(error)")
    }
}

Task {
    await displayData()
}

等待多个异步操作

使用 asyncawait,可以并行等待多个异步操作:

func fetchMultipleData() async {
    async let data1 = fetchData()
    async let data2 = fetchData()
    let combinedData = await "\(data1), \(data2)"
    print(combinedData)
}

Task {
    await fetchMultipleData()
}

使用 Task

Task 可以启动异步任务。 Task 可在任意上下文中使用并且不需要 async 上下文。

Task {
    let data = await fetchData()
    print(data)
}

使用 MainActor

在需要更新 UI 的地方,可以使用 @MainActor 来确保代码在主线程上执行:

@MainActor
func updateUI(with data: String) {
    // 更新 UI 的代码
}

func fetchDataAndUpdateUI() async {
    let data = await fetchData()
    await updateUI(with: data)
}

Task {
    await fetchDataAndUpdateUI()
}

任务取消

可以检查任务是否被取消以进行响应:

func fetchData() async throws -> String {
    for i in 1...10 {
        try Task.checkCancellation()
        print("Fetching \(i)")
        await Task.sleep(500_000_000) // 等待0.5秒
    }
    return "数据已获取"
}

func fetchDataAndHandleCancellation() async {
    do {
        let data = try await fetchData()
        print(data)
    } catch is CancellationError {
        print("任务已取消")
    } catch {
        print("获取数据时出错: \(error)")
    }
}

let task = Task {
    await fetchDataAndHandleCancellation()
}

// 模拟取消任务
task.cancel()

与传统回调模式的对比

传统回调模式:

func fetchData(completion: @escaping (String) -> Void) {
    DispatchQueue.global().async {
        sleep(2)
        completion("数据已获取")
    }
}

fetchData { data in
    print(data)
}

使用 asyncawait

func fetchData() async -> String {
    await Task.sleep(2 * 1_000_000_000)
    return "数据已获取"
}

Task {
    let data = await fetchData()
    print(data)
}

总结

asyncawait 使得 Swift 中的异步代码更加易读和易于维护,减少了传统回调和嵌套闭包的复杂性。这种方法强调了异步操作的顺序执行,使代码更接近同步代码的风格,从而更容易理解和调试。

注意事项:

  • 只有 Swift 5.5 及以上版本才支持 asyncawait
  • 这些特性在支持并发的上下文中才能使用,如 Task 或者在标记为 async 的函数内。
  • 只有 Swift 5.5 及以上版本才支持 @MainActor
  • Xcode 13: 提供了对 Swift 5.5 和其并发特性的支持。