如何实现带async get与sync getOrNil的AsyncLazy原语/包装器?
实现AsyncLazy协议的方案
核心思路
要满足「至多一次异步计算、同步返回当前结果(未完成则返回nil)、异步等待结果」的需求,我们需要线程安全地管理计算状态,确保多线程/Task环境下不会重复触发计算,同时支持两种获取方式。
状态定义
首先定义三种状态来跟踪计算进度:
private enum AsyncLazyState<T: Sendable> { case uninitialized // 未开始计算 case computing(Task<T, Never>) // 正在计算(保存计算任务) case completed(T) // 计算完成(保存结果) }
完整实现
我们使用ManagedAtomic(来自SwiftAtomics库)来原子性管理状态,结合Task封装异步计算:
import Atomics // 修正原协议的语法问题,确保符合Swift规范 public protocol AsyncLazy: Sendable { associatedtype T: Sendable var valueOrNil: T? { get } var value: T { get async } } public final class AsyncLazyImpl<T: Sendable>: AsyncLazy { private let compute: @Sendable () async -> T private let state = ManagedAtomic<AsyncLazyState<T>>(.uninitialized) public init(_ compute: @escaping @Sendable () async -> T) { self.compute = compute } public var valueOrNil: T? { switch state.load(ordering: .relaxed) { case .completed(let value): return value case .uninitialized, .computing: return nil } } public var value: T { get async { while true { let currentState = state.load(ordering: .acquiring) switch currentState { case .completed(let value): // 已完成,直接返回结果 return value case .uninitialized: // 创建计算任务,尝试切换状态为computing let task = Task { await compute() } if state.compareExchange( expected: .uninitialized, desired: .computing(task), ordering: .acquiringAndReleasing ).exchanged { // 状态切换成功,等待任务完成后更新状态 let result = await task.value state.store(.completed(result), ordering: .releasing) return result } // 切换失败,说明其他线程已开始计算,继续循环重试 case .computing(let task): // 已有计算任务,直接等待其结果 return await task.value } } } } }
关键并发原语说明
ManagedAtomic:用于原子性地读取、更新状态,避免多线程下的竞态条件。通过内存顺序参数(.acquiring/.releasing)保证状态变更的可见性,确保所有线程能获取到最新状态。Task:封装异步计算逻辑,确保计算仅执行一次。当多个异步请求同时触发时,会复用同一个Task等待结果。@Sendable:确保计算闭包和实例本身可以安全地在不同Task/线程间传递,符合Swift并发安全要求。
使用示例
// 创建AsyncLazy实例,传入异步计算闭包 let someValue = AsyncLazyImpl { // 模拟耗时异步操作 try? await Task.sleep(nanoseconds: 1_000_000_000) return "最终计算结果" } // 主线程同步获取(此时计算未完成,返回nil) let x = someValue.valueOrNil print(x) // 输出: nil // 异步线程等待获取结果 Task { let y = await someValue.value print(y) // 输出: 最终计算结果 } // 计算完成后,同步获取即可得到结果 DispatchQueue.main.asyncAfter(deadline: .now() + 2) { let z = someValue.valueOrNil print(z) // 输出: 最终计算结果 }
注意事项
- 需要通过Swift Package Manager引入
SwiftAtomics依赖(若使用Swift 5.9+,也可替换为标准库的Atomic类型)。 - 计算闭包必须是
@Sendable的,确保可以在Task中安全执行。 AsyncLazyImpl是Sendable类型,可安全用于多并发场景。
内容的提问来源于stack exchange,提问作者Caleb Jares
相关产品推荐
相关产品推荐

