Ответ
std::mutex — это примитив синхронизации, обеспечивающий взаимное исключение (mutual exclusion). Его единственная цель — гарантировать, что в один момент времени критическую секцию кода выполняет не более одного потока. Он не предоставляет механизма для ожидания какого-либо условия.
std::condition_variable — это механизм оповещения между потоками. Он позволяет одному или нескольким потокам блокироваться в ожидании выполнения некоторого условия, связанного с общими данными. Ключевой момент: condition_variable всегда работает в паре с mutex.
Простая аналогия: Mutex — это ключ от комнаты (только один человек может войти). Condition Variable — это звонок будильника, который говорит ждущему снаружи человеку: "Теперь можно проверить, свободна ли комната".
Типичный шаблон использования condition variable в C++:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
bool finished = false;
// Поток-производитель
void producer() {
for (int i = 0; i < 10; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
{
std::lock_guard<std::mutex> lock(mtx);
data_queue.push(i);
std::cout << "Produced: " << i << std::endl;
} // lock_guard выходит из области видимости, мьютекс освобождается
cv.notify_one(); // Оповещаем одного потребителя
}
{
std::lock_guard<std::mutex> lock(mtx);
finished = true;
}
cv.notify_all(); // Оповещаем всех потребителей о завершении
}
// Поток-потребитель
void consumer(int id) {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
// 1. Освобождаем мьютекс и ждём сигнала.
// 2. При получении сигнала снова захватываем мьютекс.
// 3. Проверяем условие: если очередь не пуста ИЛИ работа завершена, выходим из ожидания.
cv.wait(lock, [] {
return !data_queue.empty() || finished;
});
// Если работа завершена и очередь пуста — выходим.
if (finished && data_queue.empty()) {
break;
}
// Извлекаем данные из очереди.
int value = data_queue.front();
data_queue.pop();
lock.unlock(); // Явно отпускаем мьютекс перед долгой обработкой.
std::cout << "Consumer " << id << " processed: " << value << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
int main() {
std::thread prod(producer);
std::thread cons1(consumer, 1);
std::thread cons2(consumer, 2);
prod.join();
cons1.join();
cons2.join();
return 0;
}
Итоговое сравнение:
- Mutex решает проблему конкуренции (race condition).
- Condition Variable решает проблему координации (coordination/signaling), позволяя потокам эффективно ждать событий, не занимая CPU (busy-wait).