← Назад к вопросам

В чём разница между mutex и Condition Variables?

2.0 Middle🔥 161 комментариев
#Многопоточность и синхронизация

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Mutex и Condition Variables

Это два связанных, но разных примитива синхронизации. Хотя они часто работают вместе, их роли принципиально отличаются.

Что такое Mutex?

Mutex (Mutual Exclusion) — это замок для защиты доступа к общему ресурсу. Его основная задача:

  • Исключить одновременный доступ (только один поток может держать mutex в момент времени)
  • Предотвратить race conditions при чтении/изменении данных

Пример:

#include <mutex>

std::mutex mtx;
int shared_counter = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);  // LOCK
    shared_counter++;  // безопасно
}  // UNLOCK при разрушении lock_guard

Что такое Condition Variable?

Condition Variable — это механизм для синхронизации на уровне событий. Её назначение:

  • Позволить одному потоку уведомить другой о наступлении события
  • Блокировать поток, пока определённое условие не станет истинным
  • Избежать busy-waiting (постоянного опроса)

Пример:

#include <condition_variable>
#include <mutex>
#include <thread>

std::mutex mtx;
std::condition_variable cv;
bool data_ready = false;

void producer() {
    {
        std::lock_guard<std::mutex> lock(mtx);
        data_ready = true;
    }
    cv.notify_one();  // Сигнал потребителю
}

void consumer() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [] { return data_ready; });  // Ждём сигнала
    // Обрабатываем данные
}

Ключевые отличия

АспектMutexCondition Variable
НазначениеСинхронизация доступа к ресурсуСинхронизация на уровне событий
Основная рольВзаимное исключениеОжидание/сигнализация
Используется с-Mutex (всегда!)
wait()НетДа, блокирует поток
notify()НетДа, пробуждает потоки
Busy-waiting?ВозможенНет

Почему нужны оба?

Condition Variable требует mutex для:

  1. Защиты флага (например, data_ready)
  2. Гарантии атомарности проверки условия и ожидания
  3. Избегания race condition между проверкой и wait()
// ❌ НЕПРАВИЛЬНО — race condition
while (!data_ready) {  // Проверили
    cv.wait(mtx);     // Между check и wait может измениться!
}

// ✅ ПРАВИЛЬНО — атомарно
cv.wait(lock, [] { return data_ready; });

Практический пример: Producer-Consumer

std::queue<int> buffer;
std::mutex mtx;
std::condition_variable cv;
const int MAX_SIZE = 10;

void producer(int id) {
    for (int i = 0; i < 5; i++) {
        std::unique_lock<std::mutex> lock(mtx);
        
        // Ждём, пока буфер не опустеет
        cv.wait(lock, [] { return buffer.size() < MAX_SIZE; });
        
        buffer.push(id * 100 + i);
        std::cout << "Produced: " << buffer.back() << std::endl;
        
        lock.unlock();
        cv.notify_all();  // Уведомляем потребителей
        std::this_thread::sleep_for(std::chrono::ms(100));
    }
}

void consumer(int id) {
    for (int i = 0; i < 5; i++) {
        std::unique_lock<std::mutex> lock(mtx);
        
        // Ждём, пока буфер не заполнится
        cv.wait(lock, [] { return !buffer.empty(); });
        
        int val = buffer.front();
        buffer.pop();
        std::cout << "Consumed: " << val << std::endl;
        
        lock.unlock();
        cv.notify_all();  // Уведомляем производителей
    }
}

Когда что использовать?

  • Только Mutex: защита простого доступа к данным (счётчик, флаг)
  • Mutex + Condition Variable: когда один поток должен дождаться события другого (буфер, задача выполнена, данные загружены)

Запомни: CV без mutex = undefined behaviour. Mutex без CV = либо busy-waiting, либо неправильная синхронизация событий.

В чём разница между mutex и Condition Variables? | PrepBro