Ch 36: std::sync::Once - 一次性初始化
std::sync::Once提供了一次性初始化的能力,确保某个初始化代码只会被执行一次,常用于实现单例模式或延迟初始化。
2. 基本用法
Section titled “2. 基本用法”use std::sync::Once;use std::thread;
static INIT: Once = Once::new();
fn main() { // 多次调用只会执行一次 INIT.call_once(|| { println!("初始化执行!"); });
INIT.call_once(|| { println!("这不会打印"); });
println!("主线程继续执行");}3. call_once - 执行一次
Section titled “3. call_once - 执行一次”use std::sync::{Arc, Once};
static mut VALUE: u32 = 0;static INIT: Once = Once::new();
fn main() { INIT.call_once(|| { unsafe { VALUE = 42; } println!("初始化完成"); });
unsafe { println!("VALUE = {}", VALUE); }}4. 方法签名
Section titled “4. 方法签名”impl Once { pub fn new() -> Once pub fn call_once<F>(&self, f: F) where F: FnOnce() pub fn call_once_force<F>(&self, f: F) where F: FnOnce() pub fn is_completed(&self) -> bool}
impl OnceState { pub fn is_poisoned(&self) -> bool pub fn poison(&self)}5. 多线程竞争
Section titled “5. 多线程竞争”use std::sync::Once;use std::thread;
static INIT: Once = Once::new();
fn initialize() { println!("线程 {:?} 开始初始化", thread::current().id()); INIT.call_once(|| { println!("线程 {:?} 执行初始化", thread::current().id()); });}
fn main() { let handles: Vec<_> = (0..5).map(|i| { thread::spawn(move || { initialize(); println!("线程 {} 完成", i); }) }).collect();
for h in handles { h.join().unwrap(); }
println!("所有线程完成");}无论多少线程同时调用call_once,初始化代码只会执行一次。
6. call_once_force - 强制执行
Section titled “6. call_once_force - 强制执行”即使之前的初始化panic了,也强制执行新的初始化:
use std::sync::Once;use std::thread;
static INIT: Once = Once::new();
fn main() { // 第一次调用panic INIT.call_once(|| { panic!("初始化失败"); });
// call_once不会执行(因为已poison) // INIT.call_once(|| {});
// call_once_force会强制执行 INIT.call_once_force(|| { println!("强制初始化成功"); });}7. is_completed - 检查是否完成
Section titled “7. is_completed - 检查是否完成”use std::sync::Once;use std::thread;
static INIT: Once = Once::new();
fn main() { println!("完成状态: {}", INIT.is_completed());
INIT.call_once(|| { println!("初始化执行"); });
println!("完成状态: {}", INIT.is_completed());}8. OnceLock - 带值的Once
Section titled “8. OnceLock - 带值的Once”std::sync::OnceLock(Rust 1.70+)是更方便的形式:
use std::sync::OnceLock;
static INSTANCE: OnceLock<i32> = OnceLock::new();
fn get_instance() -> &'static i32 { INSTANCE.get_or_init(|| { println!("初始化中..."); 42 })}
fn main() { println!("{:?}", get_instance()); println!("{:?}", get_instance()); // 不再初始化}方法签名:
impl<T> OnceLock<T> { pub fn new() -> OnceLock<T> pub fn get(&self) -> Option<&T> pub fn set(&self, value: T) -> Result<(), T> pub fn get_or_init<F>(&self, f: F) -> &T where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E> where F: FnOnce() -> Result<T, E>}9. 单例模式实现
Section titled “9. 单例模式实现”使用Once实现线程安全的单例:
use std::sync::{Arc, OnceLock, Once};use std::thread;
struct Database { connection_string: String,}
impl Database { fn new() -> Self { Database { connection_string: "localhost:5432".to_string(), } }}
// 方式1:全局静态static DATABASE: Once = Once::new();static mut DB_PTR: *mut Database = std::ptr::null_mut();
fn get_database() -> &'static Database { unsafe { DATABASE.call_once(|| { DB_PTR = Box::into_raw(Box::new(Database::new())); }); &*DB_PTR }}
// 方式2:使用OnceLockstatic DATABASE_LOCK: OnceLock<Database> = OnceLock::new();
fn get_database2() -> &'static Database { DATABASE_LOCK.get_or_init(Database::new)}
fn main() { let db1 = get_database(); let db2 = get_database(); println!("同一个实例: {}", std::ptr::eq(db1, db2));}10. 延迟初始化
Section titled “10. 延迟初始化”use std::sync::OnceLock;
struct Config { settings: Vec<(String, String)>,}
impl Config { fn load() -> Config { println!("加载配置..."); Config { settings: vec![ ("host".to_string(), "localhost".to_string()), ("port".to_string(), "8080".to_string()), ], } }}
static CONFIG: OnceLock<Config> = OnceLock::new();
fn get_config() -> &'static Config { CONFIG.get_or_init(Config::load)}
fn main() { // 第一次访问时才加载 println!("开始使用配置"); let cfg = get_config(); println!("配置: {:?}", cfg.settings);}11. 初始化顺序
Section titled “11. 初始化顺序”use std::sync::{Once, OnceLock};
static A: OnceLock<u32> = OnceLock::new();static B: OnceLock<u32> = OnceLock::new();
fn get_a() -> u32 { *A.get_or_init(|| { let b = *B.get_or_init(|| 2); println!("初始化A (依赖B={})", b); b * 10 })}
fn get_b() -> u32 { *B.get_or_init(|| { println!("初始化B"); 1 })}
fn main() { // 懒初始化,按需加载 let b = get_b(); println!("B = {}", b);
let a = get_a(); println!("A = {}", a);}12. 注意事项
Section titled “12. 注意事项”- panic处理:如果初始化panic,后续
call_once调用会检查is_poisoned - 只执行一次:无论多少线程同时调用,只有一个会执行初始化
- 无参数传递:使用闭包初始化,不需要传递参数
- OnceLock更现代:Rust 1.70+推荐使用
OnceLock而非Once - 线程安全:初始化过程完全线程安全
13. 总结
Section titled “13. 总结”std::sync::Once核心API:
Once::new()- 创建新的Oncecall_once(f)- 执行一次初始化call_once_force(f)- 强制执行(即使之前失败)is_completed()- 检查是否完成OnceLock<T>- 带值的Once(推荐使用)
使用场景:
- 全局单例
- 延迟初始化
- 一次性配置
- 复杂资源的初始化
OnceLock(Rust 1.70+)是更现代的选择:
static INSTANCE: OnceLock<T> = OnceLock::new();INSTANCE.get_or_init(|| /* 初始化 */)