Rust threading: Difference between revisions
From wikinotes
No edit summary |
|||
Line 72: | Line 72: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote><!-- mspc::channel --> | </blockquote><!-- mspc::channel --> | ||
== mutexes == | |||
<blockquote> | |||
rust mutexes are a bit unique, in that they wrap the object you are protecting.<br> | |||
when the object falls out of scope, the lock is released. | |||
<syntaxhighlight lang="rust"> | |||
let m = Mutex::new(5); | |||
{ | |||
let mut num = m.lock().unwrap(); | |||
*num = 6; | |||
} // lock is released | |||
</syntaxhighlight> | |||
If a mutex is shared between threads, wrap it in <code>Arc<T></code> so the reference is not deleted.<br> | |||
This behaves like a <code>Rc<T></code> for threads (see [[rust pointers]] for more details). | |||
<syntaxhighlight lang="rust"> | |||
use std::thread; | |||
use std::sync::Arc; | |||
use std::sync::Mutex; | |||
fn main() { | |||
let src_counter_lock = Arc::new(Mutex::new(0)); | |||
let counter_lock = Arc::clone(&src_counter_lock); | |||
let spawn_thread = move|| { | |||
let counter_lock = Arc::clone(&src_counter_lock); | |||
thread::spawn(move|| { | |||
let mut count = counter_lock.lock().unwrap(); | |||
*count += 1; | |||
}) | |||
}; | |||
(1..=3) | |||
.map(|_| spawn_thread()) | |||
.for_each(|h| h.join().expect("unable to join thread")); | |||
println!("count: {}", counter_lock.lock().unwrap()); | |||
} | |||
</syntaxhighlight> | |||
</blockquote><!-- mutexes --> | |||
</blockquote><!-- Synchronization --> | </blockquote><!-- Synchronization --> |
Revision as of 04:47, 10 February 2023
Documentation
rust book: concurrency https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html std::sync
builtin synchronization primitives
Basics
Since rust already manages ownership semantics, you don't really need to deal with thread affinity.
Simply pass a closure to a thread, move any params to it, and call it a day.thead without outer scope access
use std::thread; use std::time::Duration; fn main() { let handle = thread::spawn(|| { for i in 1..=5 { println!("step {}/5..", i); thread::sleep(Duration::from_secs(1)); } }); handle.join().expect("unable to join thread"); }thread that moves outer-scope into thread's closure
use std::thread; use std::time::Duration; fn main() { let name = String::from("alex"); let handle = thread::spawn(move || { for _ in 1..=5 { println!("hi {}!", name); thread::sleep(Duration::from_secs(1)); } }); handle.join().expect("unable to join thread"); }
Synchronization
mspc::channel
multiple producer, single consumer, FIFO queue.
do work in threads, have eventloop in main thread.use std::thread; use std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); let handle = thread::spawn(move|| { tx.send(123).unwrap(); }); println!("hello to {}", rx.recv().unwrap()); handle.join().expect("unable to join thread"); }mutexes
rust mutexes are a bit unique, in that they wrap the object you are protecting.
when the object falls out of scope, the lock is released.let m = Mutex::new(5); { let mut num = m.lock().unwrap(); *num = 6; } // lock is releasedIf a mutex is shared between threads, wrap it in
Arc<T>
so the reference is not deleted.
This behaves like aRc<T>
for threads (see rust pointers for more details).use std::thread; use std::sync::Arc; use std::sync::Mutex; fn main() { let src_counter_lock = Arc::new(Mutex::new(0)); let counter_lock = Arc::clone(&src_counter_lock); let spawn_thread = move|| { let counter_lock = Arc::clone(&src_counter_lock); thread::spawn(move|| { let mut count = counter_lock.lock().unwrap(); *count += 1; }) }; (1..=3) .map(|_| spawn_thread()) .for_each(|h| h.join().expect("unable to join thread")); println!("count: {}", counter_lock.lock().unwrap()); }