Swift语言学习笔记 并发
作者:@zwvista
本文为作者原创,转载请注明出处:https://www.cnblogs.com/zwvista/p/17950030
目录
async await
async let
Task
AsyncStream
TaskGroup
Task Cancellation
actor
GlobalActor
MainActor
Sendable
async await
异步函数(方法,属性)是 Swift 并发所引入的一种新的函数类型。
异步函数在声明时用 async 关键字来标记。
异步函数具有中断(挂起)和恢复功能。代码中的中断点(暂停点)用 await 关键字来标记。
异步函数在中断并恢复前后所处的线程可能会有所不同。
允许调用异步函数或异步方法的场所
- 其他异步函数(方法,属性)的函数体
- main函数
- 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 编程语言 并发
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2018-01-07 Haskell语言学习笔记(68)HDBC