The std::thread module provides several functions to deal with thread programming. See std::thread functions inside the language reference, and Table 1.
Table 1:Main functions of the thread module
Method | Description |
|---|---|
| Returns an estimate of the current amount of new threads the system may support. It is not the raw number of threads the hardware system support, but an estimate based on the current load of the machine. It may thus vary between two calls of this function. |
| Returns the handle (i.e.: a |
| Blocks the thread until its token is made available. |
| Blocks the thread until its token is made available or the
duration |
| Creates a scope for spawning scoped threads. |
| Makes current thread to sleep for duration |
| Spawns a new thread. Returns a |
38.2.1Spawning a thread¶
The code of the main() function in a programm is what is run
inside the main thread of the process. We can spawn additional
threads using the thread::spawn() function:
std::thread::spawn(|| { println!("Hello!"); });38.2.2Waiting for termination¶
To make sure a spawned thread executes all its code, we need to
wait explicitly for its termination. For this, the spawn()
function returns a handle of type JoinHandle on which we can call
the join() that waits for the job to finish.
We spawn again the same thread, but this time storing the handle
returned by spawn():
let handle = std::thread::spawn(|| { println!("Hello!"); });We call join() on the thread handle:
handle.join().unwrap();38.2.3Thread’s returned value¶
The Closure of a thread may return a value:
let handle = std::thread::spawn(|| { 10 });This value will be returned by the join() method:
let result = handle.join().unwrap();
result38.2.4Returning an error¶
We may return a Result from a thread, and even use the
Question Mark Operator
inside a thread. However the type of the returned error must
declared threadsafe. The error type must implement the following
traits:
Send: because we pass an error object from one thread to another.
Sync: because the
?operator requires it, being implemented only forErrororError + Send + Sync.
We use the type
keyword to define a thread compatible error type. We also need to
have a file foo.txt already present, so we create it first:
use std::error::Error;
type ThreadErr = Box<dyn Error + Send + Sync>;We use the error type inside the return type of the thread. This
allows us to use the ? operator in the code:
let handle = std::thread::spawn(|| -> Result<std::fs::File, ThreadErr> {
let file = std::fs::File::options().read(true).open("foo.txt")?;
Ok(file)
});We wait for the thread to finish and return the File object:
let file = handle.join().unwrap().expect("Thread failure");
file38.2.5Execution ordering¶
Threads have no defined execution order. The operating system does not guarantee any execution ordering.
We are going to spawn multiple threads and see in which order they are executed. Let us create first a vector in which we will store the thread handles:
let mut handles: Vec<std::thread::JoinHandle<()>> = Vec::new();We then spawn 20 threads, passing an index to each of them. Thanks to the printed index, we may see that the order of execution is not the order of creation. Two runs of this code could not show the same order of execution:
for i in 0..20 {
let handle = std::thread::spawn(move || {
println!("Hello from thread {i}!");
std::thread::sleep(std::time::Duration::from_millis(100));
});
handles.push(handle);
}We finish by waiting each thread to terminate:
for h in handles {
h.join().unwrap();
}