Threads
Before we start writing multithreaded code, let's take a step back and talk about what threads are and why we might want to use them.
What is a thread?
A thread is an execution context managed by the underlying operating system.
Each thread has its own stack and instruction pointer.
A single process can manage multiple threads. These threads share the same memory space, which means they can access the same data.
Threads are a logical construct. In the end, you can only run one set of instructions
at a time on a CPU core, the physical execution unit.
Since there can be many more threads than there are CPU cores, the operating system's
scheduler is in charge of deciding which thread to run at any given time,
partitioning CPU time among them to maximize throughput and responsiveness.
main
When a Rust program starts, it runs on a single thread, the main thread.
This thread is created by the operating system and is responsible for running the main
function.
use std::thread; use std::time::Duration; fn main() { loop { thread::sleep(Duration::from_secs(2)); println!("Hello from the main thread!"); } }
std::thread
Rust's standard library provides a module, std::thread
, that allows you to create
and manage threads.
spawn
You can use std::thread::spawn
to create new threads and execute code on them.
For example:
use std::thread; use std::time::Duration; fn main() { let handle = thread::spawn(|| { loop { thread::sleep(Duration::from_secs(1)); println!("Hello from a thread!"); } }); loop { thread::sleep(Duration::from_secs(2)); println!("Hello from the main thread!"); } }
If you execute this program on the Rust playground
you'll see that the main thread and the spawned thread run concurrently.
Each thread makes progress independently of the other.
Process termination
When the main thread finishes, the overall process will exit.
A spawned thread will continue running until it finishes or the main thread finishes.
use std::thread; use std::time::Duration; fn main() { let handle = thread::spawn(|| { loop { thread::sleep(Duration::from_secs(1)); println!("Hello from a thread!"); } }); thread::sleep(Duration::from_secs(5)); }
In the example above, you can expect to see the message "Hello from a thread!" printed roughly five times.
Then the main thread will finish (when the sleep
call returns), and the spawned thread will be terminated
since the overall process exits.
join
You can also wait for a spawned thread to finish by calling the join
method on the JoinHandle
that spawn
returns.
use std::thread; fn main() { let handle = thread::spawn(|| { println!("Hello from a thread!"); }); handle.join().unwrap(); }
In this example, the main thread will wait for the spawned thread to finish before exiting.
This introduces a form of synchronization between the two threads: you're guaranteed to see the message
"Hello from a thread!" printed before the program exits, because the main thread won't exit
until the spawned thread has finished.
Exercise
The exercise for this section is located in 07_threads/01_threads