Skip to content

Ch 36: std::sync::Once - 一次性初始化

std::sync::Once提供了一次性初始化的能力,确保某个初始化代码只会被执行一次,常用于实现单例模式或延迟初始化。

use std::sync::Once;
use std::thread;
static INIT: Once = Once::new();
fn main() {
// 多次调用只会执行一次
INIT.call_once(|| {
println!("初始化执行!");
});
INIT.call_once(|| {
println!("这不会打印");
});
println!("主线程继续执行");
}
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);
}
}
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)
}
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,初始化代码只会执行一次。

即使之前的初始化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!("强制初始化成功");
});
}
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());
}

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>
}

使用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:使用OnceLock
static 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));
}
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);
}
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);
}
  1. panic处理:如果初始化panic,后续call_once调用会检查is_poisoned
  2. 只执行一次:无论多少线程同时调用,只有一个会执行初始化
  3. 无参数传递:使用闭包初始化,不需要传递参数
  4. OnceLock更现代:Rust 1.70+推荐使用OnceLock而非Once
  5. 线程安全:初始化过程完全线程安全

std::sync::Once核心API:

  • Once::new() - 创建新的Once
  • call_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(|| /* 初始化 */)