Blog Details
Sarfaraz Muhammad Sajib
28 Sept 2024
5 min read
A thread is a mini-program that runs inside a larger program (a process). A process is like a great factory and threads are the workers who work in it. Even though workers can have a specific task, all of them have access to the same tools and space (memory).
Threads permit running several tasks in parallel within one program. These are particularly useful when:
1. We would like to make our program faster by utilizing several CPU cores.
2. We need to execute some background operations without blocking the main thread.
3. The user interface must remain responsive even though the computations or I/O tasks execute in background.
There are two main types of threads in programming:
1. Kernel Threads: Managed by the operating system. Each thread has its own kernel-level stack and resources. These are heavy but provide isolation.
2. User Threads: Managed by user-level libraries or languages. They are lighter but can have some limitations, as they rely on user-space schedulers.
Rust primarily works with system (kernel) threads through its standard library.
The simplest way to create a new thread in Rust is using the std::thread module. Here’s a basic example:
use std::thread;
fn main() {
let handle = thread::spawn(|| {
println!("Hello from the spawned thread!");
});
handle.join().unwrap();
}
Rust uses two traits, Send and Sync, to ensure thread safety:
- Send: Types that can be transferred between threads.
- Sync: Types that can be safely accessed from multiple threads simultaneously.
By default, most types in Rust are Send, but some types (like raw pointers) are not. Here’s an example where we move data safely between threads:
use std::thread;
fn main() {
let numbers = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("Here's a vector: {:?}", numbers);
});
handle.join().unwrap();
}
Here, the move keyword ensures that the ownership of numbers is transferred to the spawned thread.
Rust’s mpsc (multiple producer, single consumer) channels allow threads to communicate by sending messages:
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hello");
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("Got: {}", received);
}
At Mediusware, for one of our clients, we developed a state-of-the-art Rust Server Agent. We vastly extended server management. We chose Rust because it guarantees fault tolerance and safety, and that is exactly what we needed.
From logging to log cleanup, running parallel tasks, to task scheduling, we have utilized threads throughout for our application.
Featuring:
Threads represent an indispensable tool that any system programmer will use. Rust's ownership model and memory-safety guarantees make threading much safer and more predictable because they exclude a lot of typical bugs associated with data-races.
Using Rust's powerful concurrency primitives, you can achieve highly concurrent applications without sacrificing safety or clarity.
Don’t worry, we don’t spam!