Concurrency
Condition Variables
Coordinate threads using std::condition_variable to wait for state changes without active busy-waiting.
Interview: Mechanics of spurious wakeups, why a predicate lambda is required, and unique_lock association.
A Condition Variable allows threads to suspend execution until they are notified of a state change. This prevents polling loops, saving CPU cycles.
Wait Predicate
Always supply a boolean condition check to wait() to prevent issues caused by spurious wakeups.
std::unique_lock
Requires std::unique_lock because the variable unlocks and locks the mutex internally during wait cycles.
Notification
Use notify_one() to wake a single waiting thread, or notify_all() to wake all of them.
Spurious Wakeups
Under certain OS implementations, a thread waiting on a condition variable can wake up without receiving a notification. This is called a spurious wakeup.
Preventative Action
To prevent this, you should always pass a verification predicate to the wait() call:
cv.wait(lock, []{ return isReady; });
This forces the thread to re-verify the state and go back to sleep if the condition has not actually been met.
Code Walkthrough
A simple Producer-Consumer coordinate signal loop using a condition variable.
#include <iostream> #include <thread> #include <mutex> #include <condition_variable>std::mutex g_mutex; std::condition_variable g_cv; bool g_dataReady = false;
void consumer() { std::unique_lock<std::mutex> lock(g_mutex);
// Wait until dataReady is true, handling spurious wakeups automatically g_cv.wait(lock, [] { return g_dataReady; });
std::cout << "Consumer: Data processed successfully!\n"; }
void producer() { { std::lock_guard<std::mutex> lock(g_mutex); g_dataReady = true; std::cout << "Producer: Data is prepared.\n"; } g_cv.notify_one(); // Wake up the consumer thread }
int main() { std::thread t1(consumer); std::thread t2(producer); t1.join(); t2.join(); return 0; }
Interview-Relevant Information
Q: Why does std::condition_variable require std::unique_lock instead of std::lock_guard?
Answer: When a thread calls wait(), the condition variable must release the associated mutex to let other threads (like the producer) acquire it and update the state. Once notified, the condition variable re-acquires the mutex before returning. std::unique_lock supports this manual locking and unlocking behavior, whereas std::lock_guard does not.
Q: What is a spurious wakeup, and how do you prevent it?
Answer: A spurious wakeup is when a thread wakes up from a waiting state without a notification signal. You prevent this by using a loop or passing a predicate function to wait(). The predicate checks the shared state flag, forcing the thread back to sleep if the signal is false.
Quick Checklist
Did you pass a predicate lambda to wait()? Did you use std::unique_lock? If yes, your condition variable usage is correct.
Use Cases
Implementing thread-safe task dispatcher queues (Producer-Consumer pattern).
Suspending worker threads until connection handshakes or startup steps are complete.
Common Mistakes
Calling wait() without a verification predicate, leaving the thread vulnerable to spurious wakeups.
Updating the state flag without holding the associated mutex, causing race conditions with the waiting thread.