惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
D
DataBreaches.Net
C
Check Point Blog
雷峰网
雷峰网
小众软件
小众软件
GbyAI
GbyAI
美团技术团队
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
WordPress大学
WordPress大学
MyScale Blog
MyScale Blog
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
Apple Machine Learning Research
Apple Machine Learning Research
Y
Y Combinator Blog
Jina AI
Jina AI
爱范儿
爱范儿
Last Week in AI
Last Week in AI
MongoDB | Blog
MongoDB | Blog
I
InfoQ
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美

博客园 - PKICA

c++ unordered_map‌底层实现 单片机 MCU,嵌入式,MPU,DSP,FPGA,PLC 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 rust线程-std::thread::park和unpark 配合Builder实现轻量级的线程挂起与唤醒 rust自定义线程属性std::thread::Builder rust线程 rust关联函数 rust高并发设计实践进阶 rust高并发设计实践 rust性能优化与安全边界 联合体union在rust和C语言中有什么区别 rust并发与异步编程 如何预防rust不良的代码设计总结 rust类型系统与零成本抽象 rust内存模型 告别大显存依赖!用 Rust 新一代深度学习框架 Burn 打造纯 CPU 文本分类推理引擎 rust类型系统标记 编译配置解答 git实用命令
rust延迟初始化原语
PKICA · 2026-05-28 · via 博客园 - PKICA

std::sync::OnceLockstd::sync::LazyLock 是 Rust 标准库中用于线程安全延迟初始化(Lazy Initialization)的核心同步原语。它们在 Rust 1.70 和 1.80 中先后被正式稳定,旨在完美替代社区常用的第三方库 once_celllazy_static


一、 核心区别速查

特性std::sync::LazyLock<T, F>std::sync::OnceLock<T>
初始化时机 创建时绑定闭包,首次解引用(Deref)时触发 创建时留空,后续显式调用 .get_or_init() 时触发
类型签名 包含闭包类型 F,写法较冗长 仅包含包裹的数据类型 T
灵活性 只能使用创建时指定的全局唯一闭包 可以在不同地方传入不同的闭包或动态参数
典型场景 静态全局配置、正则表达式编译、数据库连接池 运行时动态确定的单例、带运行时入参的延迟初始化

二、 std::sync::LazyLock 详解

LazyLock 采用结构一体化的设计思想,它将数据与其初始化闭包(生成函数)强绑定在一起。

1. 基本语法与示例

当你声明一个 LazyLock 时,必须立即提供一个初始化函数。当代码第一次通过 * 运算符对其进行解引用时,该函数才会被执行:

use std::sync::LazyLock;
use std::collections::HashMap;

// 声明全局静态变量,自动推导闭包类型
static GLOBAL_MAP: LazyLock<HashMap<&'static str, i32>> = LazyLock::new(|| {
    println!("Initializing GLOBAL_MAP..."); // 仅会打印一次
    let mut m = HashMap::new();
    m.insert("apple", 1);
    m.insert("banana", 2);
    m.into()
});

fn main() {
    // 首次访问:触发初始化逻辑
    println!("Apple price: {:?}", GLOBAL_MAP.get("apple"));
    
    // 再次访问:直接读取缓存数据,不再执行闭包
    println!("Banana price: {:?}", GLOBAL_MAP.get("banana"));
}

2. 核心原理与设计

  • 自动解引用(Deref):LazyLock 实现了 Deref trait。你不需要调用任何初始化方法,像普通变量一样使用它(或使用 *GLOBAL_MAP),Rust 编译器就会在底层悄悄地帮你安全地调用初始化逻辑。
  • 类型签名的代价:LazyLock 的完整类型其实是 LazyLock<T, F>,其中 F 是闭包的类型。由于在 static 变量中无法使用闭包类型推导,我们往往依赖 Rust 对标准库 LazyLock 的特殊简化支持,或者将初始化抽离为具体函数。

三、 std::sync::OnceLock 详解

OnceLock 采用容器化的设计思想。它初始化时是一个“空盒子”,你需要在稍后的代码中显式地往盒子里填入数据。

1. 基本语法与示例

OnceLock 不在声明时绑定闭包,而是在运行时通过 .get_or_init() 方法传入初始化闭包:

use std::sync::OnceLock;

static RUNTIME_CONFIG: OnceLock<String> = OnceLock::new();

fn init_config(env: &str) {
    // 根据运行时的入参来初始化全局变量
    RUNTIME_CONFIG.get_or_init(|| {
        format!("Config loaded for environment: {}", env)
    });
}

fn main() {
    // 模拟运行时的某种配置行为
    init_config("production");

    // 获取已经初始化好的值(若未初始化则会返回 None)
    if let Some(config) = RUNTIME_CONFIG.get() {
        println!("{}", config);
    }
    
    // 再次尝试用不同的闭包进行初始化,此处的闭包会被安全忽略,并返回已有的值
    let value = RUNTIME_CONFIG.get_or_init(|| "fallback".to_string());
    println!("Value: {}", value); // 依然输出 production 相关的配置
}

2. 核心方法

  • set(value):尝试直接填入值。如果盒子已经是满的,会返回 Err(value)
  • get():非阻塞地获取引用,如果没初始化则返回 None
  • get_or_init(|| ...):如果盒子空,执行闭包并填入;如果盒子满,直接返回已有值引用。
  • get_or_try_init(|| ...):允许闭包返回一个 Result。如果闭包返回 ErrOnceLock 会保持未初始化状态,允许下一次重新尝试初始化。

四、 核心技术内幕:它们如何保证线程安全?

无论是 LazyLock 还是 OnceLock,其底层都建立在 std::sync::Once 原语之上。

  • 双重检查锁(Double-Checked Locking):在高并发环境下,当多个线程同时第一次访问该变量时,底层会通过原子操作(Atomic)检查初始化状态。
  • 阻塞等待:率先到达的线程会成功获取内部锁并执行初始化闭包,而其他慢了一步的线程会安全地阻塞等待,直到第一个线程初始化完毕。
  • 零运行时开销:一旦初始化完成,后续的所有访问都会通过原子操作(Ordering::Acquire)直接读取数据,不再有任何锁竞争或阻塞开销。

五、 选型指南:

  • 无脑选择 LazyLock 的场景:如果你要定义一个全局静态常量(如正则表达式 Regex、固定配置项),且它的初始化逻辑是绝对固定、不依赖运行时动态入参的。它的人体工程学体验最好,直接当成普通变量用即可。
  • 必须选择 OnceLock 的场景:
    • 变量的初始化依赖于运行时才能获取到的参数(例如从命令行解析出的 port,或者读取出来的 config_path)。
    • 变量的初始化可能会失败(需要用到 get_or_try_init 捕获错误并支持重试)。
    • 需要在复杂的结构体内部实现某个字段的懒加载(OnceLock 的类型签名没有闭包,对结构体更友好)。

如果需要了解上述锁机制在单线程环境下的非线程安全高效替代品,可以随时询问标准库的 std::cell::LazyCellstd::cell::OnceCell。 

参考资料:

Struct std::sync::OnceLock

Rust: std::sync::OnceLock —— 全局变量的标准化方案