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

Что делает Mutex?

1.0 Junior🔥 121 комментариев
#Многопоточность и синхронизация

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

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

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

Что делает Mutex?

Определение

Mutex (Mutual Exclusion) — синхронизационный примитив, обеспечивающий эксклюзивный доступ к ресурсу. Только один поток может одновременно захватить mutex, остальные потоки ждут.

Основная задача

Mutex предотвращает race conditions, когда несколько потоков одновременно обращаются к одному ресурсу, приводя к неопределённому поведению.

Пример проблемы (Race Condition)

#include <iostream>
#include <thread>

using namespace std;

int counter = 0;

void increment() {
    for (int i = 0; i < 1000000; ++i) {
        counter++;  // RACE CONDITION!
    }
}

int main() {
    thread t1(increment), t2(increment), t3(increment);
    
    t1.join(); t2.join(); t3.join();
    
    cout << counter << endl;
    // Ожидаем: 3000000
    // Получим: 1523847 (разное каждый раз!)
}

Почему? Операция counter++ это три машинных инструкции:

1. LOAD counter (из памяти)
2. INC (инкрементируем)
3. STORE (обратно в память)

Потоки выполняют эти шаги одновременно и перезаписывают друг друга.

Решение с Mutex

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int counter = 0;
mutex mtx;  // Mutex для защиты

void increment() {
    for (int i = 0; i < 1000000; ++i) {
        mtx.lock();    // Захватываем
        counter++;     // Критическая секция
        mtx.unlock();  // Освобождаем
    }
}

int main() {
    thread t1(increment), t2(increment), t3(increment);
    
    t1.join(); t2.join(); t3.join();
    
    cout << counter << endl;  // Теперь: 3000000
}

RAII подход (лучше)

void increment() {
    for (int i = 0; i < 1000000; ++i) {
        lock_guard<mutex> guard(mtx);  // Автоматическое захватывание
        counter++;  // Защищённый код
    }  // Автоматическое освобождение
}

Типы Mutex в C++

1. std::mutex — базовый

mutex mtx;
mtx.lock();              // Захватить
mtx.unlock();            // Освободить
bool ok = mtx.try_lock(); // Попытка без блокировки

2. std::recursive_mutex — рекурсивный

recursive_mutex rmtx;

rmtx.lock();   // Один поток может захватить несколько раз
rmtx.lock();   // OK (counter = 2)
rmtx.unlock(); // counter = 1

3. std::timed_mutex — с timeout

timed_mutex tmtx;

if (tmtx.try_lock_for(chrono::seconds(1))) {
    tmtx.unlock();  // Захватил
} else {
    cout << "Timeout" << endl;
}

4. std::shared_mutex — читатели/писатели

shared_mutex sm;

// Несколько читателей одновременно
shared_lock<shared_mutex> rlock(sm);

// Один писатель исключительно
unique_lock<shared_mutex> wlock(sm);

Как работает Mutex

  1. LOCKED — захвачен одним потоком
  2. UNLOCKED — свободен
  3. Попытка захватить LOCKED mutex → блокировка потока
  4. Освобождение → один из ждущих потоков просыпается

Правила использования

1. Защищай только критическую секцию

// Неправильно
lock_guard<mutex> guard(mtx);
long_computation();  // 100ms
counter++;          // Только это нужно защищать

2. Минимальная критическая секция

long_computation();
{
    lock_guard<mutex> guard(mtx);
    counter++;  // Только это
}

3. Избегай nested locks

// Опасно — deadlock риск!
mtx1.lock();
mtx2.lock();

Performance

Mutex замедляет из-за:

  • Context switch потоков
  • Синхронизации памяти между CPU ядрами
  • Overhead lock/unlock операций

Поэтому держи критическую секцию максимально короткой.

Что делает Mutex? | PrepBro