You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现带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) // 输出: 最终计算结果
}

注意事项

  1. 需要通过Swift Package Manager引入SwiftAtomics依赖(若使用Swift 5.9+,也可替换为标准库的Atomic类型)。
  2. 计算闭包必须是@Sendable的,确保可以在Task中安全执行。
  3. AsyncLazyImpl是Sendable类型,可安全用于多并发场景。

内容的提问来源于stack exchange,提问作者Caleb Jares

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 01:43:36