Ответ
Плюсы мьютекса (std::mutex):
- Гарантирует взаимное исключение (mutual exclusion). Это фундаментальный примитив для защиты критических секций, обеспечивающий, что только один поток в данный момент владеет блокировкой и имеет доступ к общим данным.
- Простота и понятность модели использования. Базовый API (
lock(),unlock()) легко понять. Использование RAII-обёрток, таких какstd::lock_guardилиstd::unique_lock, делает код исключительно безопасным. - Интеграция с другими примитивами стандартной библиотеки.
std::mutexнапрямую работает сstd::condition_variableдля реализации шаблонов "ожидания по условию", что критически важно для многих многопоточных паттернов (например, producer-consumer). - Поддержка рекурсивных блокировок через
std::recursive_mutex. Позволяет одному и тому же потоку захватывать один и тот же мьютекс несколько раз, что может упростить код в некоторых сценариях (например, при вызове рекурсивных функций).
Минусы мьютекса:
- Относительно высокая стоимость операций. Если блокировка недоступна, поток переводится в состояние ожидания ядром ОС, что приводит к переключению контекста. Это дороже, чем спин-блокировка (
std::atomic_flag), если время удержания блокировки очень мало. - Риск взаимной блокировки (deadlock). Возникает при неправильном порядке захвата нескольких мьютексов разными потоками. Для борьбы с этим следует использовать
std::lock()для атомарного захвата нескольких мьютексов. - Отсутствие разделения на читателей и писателей.
std::mutexне различает операции чтения и записи. Если данные часто читаются, но редко обновляются, более эффективным может бытьstd::shared_mutex(C++17), который позволяет множеству читателей работать одновременно. - Нельзя копировать или перемещать. Объект мьютекса должен быть статическим или разделяемым через ссылку/указатель.
Пример безопасного использования с RAII:
#include <iostream>
#include <thread>
#include <mutex>
std::mutex g_console_mutex;
int g_shared_counter = 0;
void safe_increment() {
for (int i = 0; i < 10000; ++i) {
// std::lock_guard автоматически lock() в конструкторе и unlock() в деструкторе
std::lock_guard<std::mutex> lock(g_console_mutex);
++g_shared_counter; // Критическая секция защищена
// Выход из области видимости -> автоматический unlock
}
}
int main() {
std::thread t1(safe_increment);
std::thread t2(safe_increment);
t1.join();
t2.join();
std::cout << "Final counter: " << g_shared_counter << std::endl; // Всегда 20000
return 0;
}