Run Code Simultaneously
Rust provides built-in support for concurrent programming through its standard library module
std::thread
.Threads in Rust allow multiple parts of a program to execute simultaneously, leveraging multi-core processors.
Threads
You can spawn threads using
std::thread::spawn
, which takes a closure, runs it in a new thread, and returnJoinHandle
.We can wait the spawned thread to finish using
join
method from the returnedJoinHandle
.pub fn join(self) -> Result<T>
: Waits for the associated thread to finish. This function will return immediately if the associated thread has already finished.We often use the
move
keyword with closures passed tothread::spawn
because the closure will then take ownership of the values it uses from the environment, thus transferring ownership of those values from one thread to another
Example
Using Smart Pointers, Mutex, and Channels in Threads
Shared Mutable State with Arc and Mutex
In a multi-threaded program, if we need shared mutable state, we can use:
Arc<T>
:An atomic reference-counted smart pointer for shared ownership across threads.
Mutex<T>
:A synchronization primitive for mutual exclusion to safely access shared data.
lock()
: Acquires a lock. Blocks the thread if a lock is held.try_lock
(): Attempts to acquire a lock immediately. If lock cant be acquired, return anErr
.
Example
Arc<T>
: Enables shared ownership of theMutex
across threads.Mutex<T>
: Ensures only one thread accesses the counter at a time.
Multiple Readers Single Writer
`RwLock (short for Read-Write Lock) is a synchronization primitive that allows multiple readers or a single writer to access shared data. It ensures safe concurrent access in a multi-threaded environment.
Multiple Readers: Multiple threads can hold a read lock simultaneously if no thread is holding the write lock.
Single Writer: Only one thread can hold the write lock, and it has exclusive access to the data.
RwLock
is part of the std::sync
module and is frequently used when you want to share data among threads with both read and write operations while minimizing contention.
Read Lock
read()
: Acquires a read lock. Blocks the thread if a write lock is held. Returns an immutable reference to the data.try_read()
: Attempts to acquire a read lock immediately and returnErr
if cannot acquire read lock.
Write Lock
write()
: Acquires a write lock. Blocks the thread if any read or write lock is held. Returns a mutable reference to the data.try_write()
: Attempts to acquire a write lock immediately and returnErr
if cannot acquire write lock.
Example
The counter is wrapped in
Arc<RwLock<T>>
so it can be safely shared between threads.Arc
provides reference counting for thread-safe ownership.write()
: Acquires a write lock, granting exclusive mutable access to the shared counter.read()
: Acquires a read lock, allowing multiple threads to access the counter immutably.The locks ensure that data races are prevented when accessing the shared counter.
Communication Between Threads with Channels
Rust provides channels for thread communication in the std::sync::mpsc
module:
mpsc
: Stands for multiple producers, single consumer.That means we can create multiple
Sender
usingclone
method.
Example
mpsc::channel
creates a transmitter (tx
) and a receiver (rx
).Messages are sent via the Sender
tx
and received via the Receiverrx
.
Best Practices for Multithreading
Minimize Lock Contention:
Keep the critical section (where the mutex is locked) as short as possible to avoid slowing down threads.
Use
Arc
andMutex
Wisely:Avoid using shared state unless necessary.
Prefer message passing with channels for better isolation.
References
Last updated