OnceLock
Often in multithreaded Rust programs we want to initialize some data that is important for more than one thread. And it only needs to be initialized once, not many times.
With OnceLock
, we can have clear syntax for a struct
that is only created once, but shared multiple times. Creation occurs on a thread—other work can be done in parallel.
To begin we have a struct
called Test—it holds some data that requires initialization. A string
must be created. A Test struct
is contained in a static
OnceLock
.
main()
we create 8 threads and place them in a vector so they can later be joined together.spawn()
we call get_or_init()
on the TEST OnceLock
.get_or_init()
function lazily invokes the init_test()
function. This is only called once.struct
and stores it in the OnceLock
, and then every other thread accesses that static
data.use std::sync::OnceLock; use std::sync::*; use std::thread; static TEST: OnceLock<Test> = OnceLock::new(); struct Test { name: String, count: usize, } fn init_test() -> Test { // Logic to initialize the static data in the OnceLock. println!("Initializing test"); Test { name: String::from("BIRD"), count: 500, } } fn main() { let mut children = vec![]; for _ in 0..8 { children.push(thread::spawn(move || { // Initialize the data on the first access. // If already initialized, just return the existing data. let test = TEST.get_or_init(|| init_test()); println!("TEST: {}", test.name); })); } for child in children { let _result = child.join(); } println!("DONE"); }Initializing test TEST: BIRD TEST: BIRD TEST: BIRD TEST: BIRD TEST: BIRD TEST: BIRD TEST: BIRD TEST: BIRD DONE
With OnceLock
and the similar type OnceCell
(which is for non-threaded scenarios) we have a lazy-instantiation type. OnceLock
is safe on multiple threads—work can be done in parallel.