Basics
- Use
std::thread::spawnto create a thread (but not execute) xxx.join()executes a thread, wait for its completion and collect its result.- Its return type is
Result<_, _>. ReturnsErr(...)if panics,Ok(...)otherwise.
- Its return type is
move of closure
Watch the following example:
1 |
|
If you try to remove the move before the closure, you’ll get an error. The reason is that, by default, Rust will try to borrow the variables, even though its type has implemented Copy trait. So in fact, inside the closure, it’s actually &value, &ms.
Now we have a look of the signature of thread::spawn()
1 | pub fn spawn<F, T>(f: F) -> JoinHandle<T> |
The closure should have static life time, which means its arguments cannot be on the stack. However, if we do not add the move keyword, the &value, &ms indeed refers to addresses of the data on the callstack, which violates the signature. This is just to prevent cases that the function exits before the thread has completed, so that during execution, the thread is holding a now-dangling reference to the data which was originally on the callstack.
Some Advanced Topics
Named Threads
Using thread::Builder, you can create threads with name, which becomes easier to debug.
1 | use std::thread; |
Scoped Threads
Scoped threads allow borrowing stack data without moving ownership. The threads are guaranteed to complete before the scope ends, so the references inside the threads are valid.
First, use thread::scope(|s| { ... }) to create a scope s. In the closure, use s.spawn() to create scoped threads.
1 | use std::thread; |
Thread Local Variables
Each thread gets its own independent copy of a thread_local! variable. By setting the variable type as RefCell<>, you can modify the data with .borrow_mut()
1 | thread_local! { |
Arc<Mutex<T>>
By using Arc<Mutex<T>>, data can be shared and modified across threads. Usually, we need to clone Arc as loop-local varaible, move it into the thread and push the thread to a vector for later batch join().
After that, inside the closure, use .lock().unwrap() to retrieve underlying data and apply modifications.
1 | pub fn concurrent_collect(n_threads: usize) -> Vec<usize> { |
Multi Producer, Single Consumer Channels
Use std::sync::mpsc::channel() to create channels, which returns (sender, receiver). sender can be clone() to be used in multi-threading.
In addition, recv() will keep blocking before all senders are dropped.
mpsc::channel()
mpsc::channel()1 | /// Create a producer thread that sends each element from items into the channel. |