In this lesson, you will learn how to use std::sync::Condvar to make your threads sleep efficiently while waiting for a specific condition to become true, avoiding the performance drain of "busy-waiting."
The Problem with Busy-Waiting
When you need a thread to wait for a shared state to change (like a data buffer filling up), the naive approach is to loop and check a Mutex repeatedly. This is called busy-waiting. It consumes 100% of a CPU core just to ask "is it ready yet?" over and over.
The Solution: Condition Variables
A condition variable allows a thread to suspend execution (go to sleep) and relinquish its CPU time. It only wakes up when another thread signals that the state has changed.
To use a Condvar effectively, you must pair it with a Mutex. The condition variable relies on the mutex to protect the state, and it provides a mechanism to atomically release the lock while going to sleep.
Example: Waiting for a Signal
Imagine a worker thread waiting for a "ready" flag:
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair_clone = Arc::clone(&pair);
thread::spawn(move || {
let (lock, cvar) = &*pair_clone;
// Perform some work...
let mut ready = lock.lock().unwrap();
*ready = true;
cvar.notify_one(); // Wake up the waiting thread
});
// Main thread waits for the condition
let (lock, cvar) = &*pair;
let mut ready = lock.lock().unwrap();
while !*ready {
// wait() unlocks the mutex and blocks the thread
// When notified, it re-acquires the lock
ready = cvar.wait(ready).unwrap();
}
println!("State changed: ready is true!");
Why the while Loop?
You might notice the while !*ready loop. This is critical. Due to "spurious wakeups"—where a thread might wake up even without a notification—you must always re-verify the condition after waking up. The wait method automatically re-acquires the mutex lock before it returns, ensuring that when you exit the loop, you safely own the protected state again.
Key Takeaway: Use a Condvar paired with a Mutex to block threads until a specific state is reached, ensuring your program remains responsive without wasting CPU cycles on busy-waiting loops.