zwvista

导航

Swift语言学习笔记 并发

async await

异步函数(方法,属性)是 Swift 并发所引入的一种新的函数类型。
异步函数在声明时用 async 关键字来标记。
异步函数具有中断(挂起)和恢复功能。代码中的中断点(暂停点)用 await 关键字来标记。
异步函数在中断并恢复前后所处的线程可能会有所不同。

允许调用异步函数或异步方法的场所

  1. 其他异步函数(方法,属性)的函数体
  2. main函数
  3. Task 闭包中的代码
// 声明异步函数时使用 async
func listPhotos(inGallery name: String) async -> [String] {
    let result = // ... some asynchronous networking code ...
    return result
}
// 调用异步函数时使用 await
// 调用 Task.yield 方法可以提交控制权
func generateSlideshow(forGallery gallery: String) async {
    let photos = await listPhotos(inGallery: gallery)
    for photo in photos {
        // ... render a few seconds of video for this photo ...
        await Task.yield()
    }
}
// 声明会抛异常的异步函数时使用 async 和 throws
// 调用会抛异常的异步函数时使用 try 和 await
// 调用 Task.sleep 方法可以让异步函数中断执行一定时间
func listPhotos(inGallery name: String) async throws -> [String] {
    try await Task.sleep(for: .seconds(2))
    return ["IMG001", "IMG99", "IMG0404"]
}

async let

// 同步执行,依次下载三张图片
let firstPhoto = await downloadPhoto(named: photoNames[0])
let secondPhoto = await downloadPhoto(named: photoNames[1])
let thirdPhoto = await downloadPhoto(named: photoNames[2])
let photos = [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
// 异步执行,同时下载三张图片
async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])
let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)

Task

Swift 的并发代码通常在 Task (任务)中执行
启动新的 Task 可以使用 Task.init 方法(在当前 actor 中执行)或 Task.detached 方法(不在当前 actor 中执行)
Task 的作用与 GCD 中的 DispatchQueue.global().async/sync 相当

// 调用 Task.init 方法启动带返回值的 Task
// 使用 await 等待 Task 执行完成并取得结果
let newPhoto = // ... some photo data ...
let handle = Task {
    return await add(newPhoto, toGalleryNamed: "Spring Adventures")
}
let result = await handle.value

AsyncStream

TaskGroup

TaskGroup 可以用来创建具有父子关系的任务组
任务组中的子任务必须具有相同类型,即所有子任务都返回相同类型的结果。
任务组通常用来并发执行个数不确定的多个子任务
TaskGroup 的作用与 GCD 中的 DispatchGroup 相当

// 创建任务组来同时下载个数不确定的图片
await withTaskGroup(of: Data.self) { group in
    let photoNames = await listPhotos(inGallery: "Summer Vacation")
    for name in photoNames {
        group.addTask {
            return await downloadPhoto(named: name)
        }
    }
    for await photo in group {
        show(photo)
    }
}

Task Cancellation

Task 在启动之后可以被取消

let task = await Task.withTaskCancellationHandler {
    // ...
} onCancel: {
    print("Canceled!")
}
// ... some time later...
task.cancel()  // Prints "Canceled!"

actor

actor 是 Swift 并发所引入的一种新的数据类型。
可以把 actor 看成线程安全的 class,使用 actor 可以防止数据竞争。
actor 是引用类型,但是 actor 不可继承。
actor 的属性以及方法缺省具有 isolated(隔离)特性。
在读取 actor 中具有隔离特性的属性以及调用 actor 中具有隔离特性的方法时需要用 await 来标记。
不能在 actor 之外更新 actor 中具有隔离特性的属性。
可以使用 nonisolated 关键字来标记不需要隔离特性的属性或方法。
actor 的作用与 GCD 中的 DispatchQueue.init 相当

// actor 本质上是一种线程安全的 class 
actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    private(set) var max: Int

    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}
// 在 actor 之外读取 actor 的属性时需要 await
let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max)
// Prints "25"

// actor 中的方法在存取 actor 自己的属性时不需要 await
extension TemperatureLogger {
    func update(with measurement: Int) {
        measurements.append(measurement)
        if measurement > max {
            max = measurement
        }
    }
}

GlobalActor

MainActor

需要在 main 线程中运行的代码(比如 UI 的更新)以及使用的数据属性可以使用 MainActor 标记
MainActor 是一种预定义的 GlobalActor。
MainActor 通常用来标记与 UI 密切相关的 ViewModel 类
MainActor 的作用与 GCD 中的 DispatchQueue.main.sync/async 相当

// 使用 MainActor 标记 ViewModel 类,该类需要在 main 线程中被使用
@MainActor
final class HomeViewModel {
    // ..
}
// 使用 MainActor 标记 ViewModel 类中的数据成员,该数据成员需要在 main 线程中被使用
final class HomeViewModel {
    @MainActor var images: [UIImage] = []
}
// 使用 MainActor 标记函数,该函数需要在 main 线程中被执行
@MainActor
func fetchImage(for url: URL) async throws -> UIImage {
    let (data, _) = try await URLSession.shared.data(from: url)
    guard let image = UIImage(data: data) else {
        throw ImageFetchingError.imageDecodingFailed
    }
    return image
}
// 直接调用 MainActor.run 方法在 main 线程中执行更新 UI 的代码
Task {
    await someHeavyBackgroundOperation()
    await MainActor.run {
        // Perform UI updates
    }
}
// 使用 MainActor 标记 Task,该任务中的代码需要在 main 线程中被执行
Task { @MainActor in
    // Perform UI updates
}

Sendable

https://www.avanderlee.com/swift/async-await/
Swift 中的 async/await
Swift 编程语言 并发

posted on 2024-01-07 08:05  zwvista  阅读(154)  评论(0编辑  收藏  举报