Concurrency
Concurrency is the mechanism of handling tasks in parallel. This helps use the available system resources such as CPUs more efficiently. As code runs in different contexts in threads, passing messages and handling shared variables becomes an important part of the implementation. Rust's standard library provides solutions to these concurrency needs out of the box.
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.
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(); }
||: 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.
Message Passing
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); }
- channels from
std::sync::mpscmodule can be used for message passing between threads
Shared State
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
Arcis created to manage the shared ownership of aMutexthat guards an integer counter initialized to 0. -
A vector
handlesis created to store the handles of the spawned threads. -
A loop is used to spawn 10 threads. Each thread:
- Clones the
Arcpointer to get a new reference to the sharedMutex. - Locks the
Mutexto get mutable access to the counter. - Increments the counter by 1.
- Clones the
-
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.