One of the goals of Rust is to make concurrency as safe as can be.
Example:
use std::sync::mpsc; // Multiple producer, single consumer (mpsc)
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
// Transmitter and Receiver
let (tx, rx) = mpsc::channel();
// Mutex must be stored an atomic shared pointer to share between threads
let counter = Arc::new(Mutex::new(0));
// Threads list
let mut handles = vec![];
for i in 0..=10 {
let tx2 = tx.clone();
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
// Sleep for 1 millisecond
thread::sleep(Duration::from_millis(100));
// Will release the lock at the end of the scope
let mut num = counter.lock().unwrap();
*num += 1;
// Transmit string to reciever
tx2.send(format!("Thread number: {}, Mut number: {}", i, num)).unwrap();
});
handles.push(handle);
}
// Received will end when all tx go out of scope
// tx acts like a shared pointer, keeping track of references
drop(tx);
for received in rx {
println!("{}", received);
}
// Wait for threads to stop
for handle in handles {
handle.join().unwrap();
}
println!("DONE!");
}
Channels allow threads to communicate with one another. A channel consists of a transmitter and a receiver. Given a channel, there can be any number of transmitters but only one receiver.
move
allows you to move local variable's ownership to a closure.
use std::thread;
fn main() {
let v = vec![0, 1, 2, 3];
let handle = thread::spawn(move || {
println!("{:?}", v);
});
// Cannot use v in main thread anymore
handle.join().unwrap();
}
Mutexes allows thread safe access to memory. They should be pared with Arc<T>
(atomic Rc<T>
) to be shared amount threads.
Any type must implment the std::marker
traits Sync
and Send
to be passed into a thread. Any type that does not use non-thread safe pointers automatically implements these traits.