Post

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

FunctionDescription
notify_onewakes one thread
notify_allwakes 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.