Concurrency in Rust
Rust provides several ways to handle concurrency, ensuring safety and performance. Here are some key concepts and examples:
1. Threads
Rust's standard library provides a way to spawn threads using the std::thread
module. The thread::spawn
function is used to create a new thread. The || syntax is used to define a closure, which is an anonymous function that can capture variables from its surrounding scope.
Here's a breakdown of the syntax:
||
: This defines a closure with no parameters. If the closure had parameters, they would be listed between the vertical bars.
{ ... }
: This is the body of the closure, which contains the code that will be executed in the new thread.
use std::thread; macro_rules! sleep { ($millis:expr) => { std::thread::sleep(std::time::Duration::from_millis($millis)); }; } fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!("hi number {} from the spawned thread!", i); sleep!(50); } }); for i in 1..5 { println!("hi number {} from the main thread!", i); sleep!(50); } handle.join().unwrap(); }
2. Message Passing
Rust uses channels for message passing between threads, provided by the std::sync::mpsc
module.
use std::sync::mpsc; use std::thread; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let val = String::from("hi"); tx.send(val).unwrap(); }); let received = rx.recv().unwrap(); println!("Got: {}", received); }
3. Shared State
Rust ensures safe access to shared state using Arc (Atomic Reference Counting) and Mutex (Mutual Exclusion) from the std::sync
module.
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap()); }
An atomic reference-counted pointer Arc
is created to manage the shared ownership of a Mutex
that guards an integer counter initialized to 0.
A vector handles
is created to store the handles of the spawned threads.
A loop is used to spawn 10 threads. Each thread:
- Clones the
Arc
pointer to get a new reference to the sharedMutex
. - Locks the
Mutex
to get mutable access to the counter. - Increments the counter by 1.
Another loop is used to join all the spawned threads, ensuring that the main thread waits for all the threads to finish execution.
Finally, the value of the counter is printed, which should be 10 if all threads have successfully incremented the counter.