Sharing Data with Read/Write Locks
It's a really common pattern to have some data that changes infrequently, mostly accessed by worker threads---but occasionally, you need to change it.
The code for this is in
rwlock
in thecode/02_threads
directory.
We're going to use once_cell
in this example, so add it with cargo add once_cell
. once_cell
is on its way into the standard library.
Let's build a simple example of this in action:
use std::sync::RwLock; use once_cell::sync::Lazy; static USERS: Lazy<RwLock<Vec<String>>> = Lazy::new(|| RwLock::new(build_users())); fn build_users() -> Vec<String> { vec!["Alice".to_string(), "Bob".to_string()] } // Borrowed from last week! pub fn read_line() -> String { let mut input = String::new(); std::io::stdin() .read_line(&mut input) .expect("Failed to read line"); input.trim().to_string() } fn main() { std::thread::spawn(|| { loop { println!("Current users (in a thread)"); let users = USERS.read().unwrap(); println!("{users:?}"); std::thread::sleep(std::time::Duration::from_secs(3)); } }); loop { println!("Enter a name to add to the list (or 'q' to quit):"); let input = read_line(); if input == "q" { break; } let mut users = USERS.write().unwrap(); users.push(input); } }
Notice that we've used the Lazy
pattern we talked about last week: the static variable is only initialized when someone looks at it.
We've wrapped the list of names in an RwLock
. This is like a Mutex
, but you can either read or write to it. You can have multiple readers, but only one writer.
Uncontested read is very fast. Pausing for a write is very slightly slower than a Mutex
, but not by much.