68
edits
Changes
→Parallelism with Rust
== Locks (Mutex) ==
== RC and Atomic ==
In Rust, some data types are defined as “thread safe” while others are not. For example, Rc<T> type, which is Rust’s own implementation of “smart pointer”, is considered to be unsafe to share across threads. This type keeps track of number of references and increments/decrements count each time a new reference is created or old one gets destroyed. Rc<T> does not enforce any mechanisms that make sure that changes to the reference counter value can’t be interrupted by another thread. The snippet below demonstrates the issue an results in compiler error: <source lang="rust">let mutex = Rc::new(Mutex::new(2));let mut handles = vec![text]; for _ in 0..4 { let mutex = mutex.clone(); let handle = thread::spawn(move || { let mut num = mutex.lock().unwrap(); *num *= 2; println!("Intermediate Result : {}", *num); }); handles.push(handle);} for handle in handles { handle.join().unwrap(); } println!("Final Result: {}", *mutex.lock().unwrap());</source> This will produce an error : <source lang="rust">error[E0277]: the trait bound `std::rc::Rc<std::sync::Mutex<i32>>: std::marker::Send` is not satisfied in `[closure@src/main.rs:11:32: 16:6 mutex:std::rc::Rc<std::sync::Mutex<i32>>]` --> src/main.rs:11:18 |11 | let handle = thread::spawn(move || { | ^^^^^^^^^^^^^ `std::rc::Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely | = help: within `[closure@src/main.rs:11:32: 16:6 mutex:std::rc::Rc<std::sync::Mutex<i32>>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::sync::Mutex<i32>>` = note: required because it appears within the type `[closure@src/main.rs:11:32: 16:6 mutex:std::rc::Rc<std::sync::Mutex<i32>>]` = note: required by `std::thread::spawn` </source> Luckily for us, Rust ships another thread-safe implementation of ‘smart pointer’ called Arc<T> which implements reference counting via atomic operations. It is important to note that in serial code, it makes much more sense to use standard Rc<T> over Arc<T> type since the latter structure is more expensive performance-wise. <source lang="rust">use std::sync::{Arc, Mutex};use std::thread; fn main() {let mutex = Arc::new(Mutex::new(2));let mut handles = vec![]; for _ in 0..4 { let mutex = mutex.clone(); let handle = thread::spawn(move || { let mut num = mutex.lock().unwrap(); *num *= 2; println!("Intermediate Result : {}", *num); }); handles.push(handle);} for handle in handles { handle.join().unwrap(); } println!("Final Result: {}", *mutex.lock().unwrap()); } </source> This will produce the expected result : <source>Intermediate Result : 4Intermediate Result : 8Intermediate Result : 16Intermediate Result : 32Final Result: 32</source>
This time the code worked. This example was very simple and not too impressive. Much more complicated algorithms can be implemented with the Mutex<T>.
== Group Members ==