std::condition_variable
std::condition_variable
std::condition_variable
Prerequisites
1. What is condition_variable?
A std::condition_variable is a synchronization primitive that allows threads to:
- wait until a condition becomes true
- be notified by another thread
condition_variable lets threads sleep efficiently and wake up when work is available.
2. Why Do We Need It?
❌ Bad Approach (Busy Waiting)
1
while (queue.empty()) {}
1
2
- CPU usage is high
- Wasteful spinning
✔️ Better Approach
1
cv.wait(lock, condition);
1
2
- Thread sleeps (no CPU usage)
- Wakes only when needed
Basic Concept
1
2
3
Producer → produces data
Consumer → waits for data
condition_variable → notifies consumer
1
2
cv.wait() → sleep
while loop → spin
3. Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex m;
std::condition_variable cv;
std::queue<int> q;
void producer()
{
{
std::lock_guard<std::mutex> lock(m);
q.push(42);
}
cv.notify_one(); // wake one waiting thread
}
void consumer()
{
std::unique_lock<std::mutex> lock(m);
auto lmdCondition = []() { return !q.empty(); };
cv.wait(lock, lmdCondition);
int value = q.front();
q.pop();
std::cout << value << std::endl;
}
int main()
{
std::thread t1(consumer);
std::thread t2(producer);
t1.join();
t2.join();
}
How wait() Works Internally
1
2
3
4
5
1. lock is released
2. thread goes to sleep
3. notify_one() wakes it
4. lock is reacquired
5. condition is checked again
Important Rule (Predicate)
Always use a condition:
1
cv.wait(lock, [] { return !q.empty(); });
1
2
- Avoid spurious wakeups
- Ensure condition is actually satisfied
notify_one vs notify_all
| Function | Description |
|---|---|
| notify_one | wakes one thread |
| notify_all | wakes all waiting threads |
1
2
cv.notify_one(); // single worker
cv.notify_all(); // broadcast
✔️ Not a Lock
1
2
condition_variable does NOT protect data
→ mutex is required
✔️ Used with mutex
1
2
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, condition);
✔️ Event-driven
1
Thread sleeps → wakes on notification
4. Common Mistakes
❌ Missing predicate
1
cv.wait(lock); // dangerous
❌ Holding lock too long
1
lock → long computation → unlock ❌
Bad Example
1
2
3
4
std::lock_guard<std::mutex> lock(m);
shared_data = prepare_input();
long_computation(); // too long
shared_result = finalize();
Better Example
1
2
3
4
5
6
7
8
9
10
11
12
13
Better Example
int local_copy;
{
std::lock_guard<std::mutex> lock(m);
local_copy = shared_data;
}
long_computation(local_copy); // No lock
{
std::lock_guard<std::mutex> lock(m);
shared_result = local_copy;
}
❌ Forget notify
1
thread sleeps forever
This post is licensed under CC BY 4.0 by the author.