Threads are fundamental units of execution within a process that allow a program to perform multiple operations concurrently. In essence, they enable different parts of your code to run "at the same time" (either truly in parallel on multi-core processors or interleaved by the operating system's scheduler on single-core processors), enhancing responsiveness, utilizing system resources more efficiently, and potentially improving performance for certain types of tasks.
Why Use Threads?
1. Concurrency and Responsiveness: Keep an application's user interface responsive while long-running tasks (like network requests, file processing, or complex calculations) are being handled in the background on separate threads.
2. Performance: On systems with multiple CPU cores, threads can genuinely run in parallel, allowing CPU-bound tasks to complete faster by distributing the workload across available cores.
3. Resource Utilization: Efficiently utilize I/O resources by allowing one thread to perform computation while another waits for I/O operations to complete.
Threads in Rust
Rust provides threading capabilities through its standard library, specifically the `std::thread` module.
* `thread::spawn`: This is the primary function to create a new thread. It takes a closure (`FnOnce`) as an argument, which contains the code that the new thread will execute. The closure must have a `'static` lifetime, meaning any data it captures must either live for the entire duration of the program or be owned by the thread itself.
```rust
use std::thread;
thread::spawn(|| {
// Code to run in the new thread
println!("Hello from a new thread!");
});
```
* `JoinHandle`: When you call `thread::spawn`, it returns a `JoinHandle<T>`, where `T` is the type of the value that the thread's closure returns. This handle is a token that represents the spawned thread. You can use it to wait for the thread to complete its execution and to retrieve the value it returned.
* `join()` method: The `join()` method on a `JoinHandle` is crucial for synchronization. When `join()` is called, the current thread (typically the main thread) will block its execution until the spawned thread associated with that `JoinHandle` has finished. This prevents the main thread from exiting prematurely, which would forcibly terminate all other running threads. The `join()` method returns a `Result<T, Box<dyn Any + Send>>`, where `Ok(T)` contains the value returned by the thread's closure, and `Err` indicates that the thread panicked.
Data Sharing and Safety
Rust's ownership and borrowing system is a powerful tool for preventing common concurrency bugs like data races at compile time. While threads can share data, Rust ensures this is done safely. For shared mutable state between threads, you'll typically use mechanisms like `Arc` (Atomically Reference Counted) for shared ownership and `Mutex` (Mutual Exclusion) for controlled mutable access. For this basic example, we will focus on threads performing independent work without explicit shared mutable state.
Example Code
```rust
use std::thread;
use std::time::Duration;
fn main() {
println!("Main thread: Starting program.");
// --- Spawn the first thread ---
// This thread will count up to 5 with a delay.
let handle1 = thread::spawn(|| {
for i in 1..=5 {
println!("Thread 1: Counting {}", i);
// Simulate some work or a delay
thread::sleep(Duration::from_millis(500));
}
"Thread 1 finished its work!" // This string is returned by the thread
});
// --- Spawn the second thread ---
// This thread will iterate a few times with a different delay.
let handle2 = thread::spawn(|| {
for i in 1..=3 {
println!("Thread 2: Iteration {}", i);
// Simulate some work or a delay
thread::sleep(Duration::from_millis(700));
}
"Thread 2 also completed!" // This string is returned by the thread
});
// --- Main thread doing its own work ---
// The main thread can continue executing while handle1 and handle2 run concurrently.
for i in 1..=2 {
println!("Main thread: Doing its own important work {}", i);
thread::sleep(Duration::from_millis(600));
}
// --- Waiting for threads to finish ---
// Call .join() on the handles to wait for the spawned threads to complete.
// This is crucial to ensure the main thread doesn't exit before the spawned threads are done.
println!("Main thread: Waiting for Thread 1 to finish...");
match handle1.join() {
Ok(message) => println!("Main thread: Received from Thread 1: {}", message),
Err(e) => eprintln!("Main thread: Thread 1 panicked: {:?}", e),
}
println!("Main thread: Waiting for Thread 2 to finish...");
match handle2.join() {
Ok(message) => println!("Main thread: Received from Thread 2: {}", message),
Err(e) => eprintln!("Main thread: Thread 2 panicked: {:?}", e),
}
println!("Main thread: All threads have completed. Program ending.");
}
```








Using Threads to Run Code Simultaneously in Rust