В чем разница между std::mutex и std::atomic?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между std::mutex и std::atomic
std::mutex и std::atomic — это два разных механизма синхронизации, предназначенные для разных сценариев.
std::mutex — блокирующая синхронизация
std::mutex — это примитив взаимного исключения (mutual exclusion), который использует блокировку для защиты общего ресурса. Когда поток попытается захватить mutex, если он уже занят другим потоком, первый поток блокируется и ждёт освобождения.
#include <mutex>
int counter = 0;
std::mutex mtx;
void increment() {
std::lock_guard<std::mutex> lock(mtx);
counter++; // защищённый доступ
}
Основные характеристики:
- Блокирует поток — если ресурс занят, поток переходит в режим ожидания
- Работает с любыми типами — защищает критические секции кода
- Может быть nested — если правильно использовать
std::recursive_mutex - Тяжелее — требует переключения контекста ОС
std::atomic — lock-free синхронизация
std::atomic — это шаблон для создания атомарных типов, операции над которыми гарантированно неделимы (atomic) без явного использования блокировок. Процессор обеспечивает атомарность на уровне инструкций.
#include <atomic>
std::atomic<int> counter(0);
void increment() {
counter++; // атомарная операция
}
Основные характеристики:
- Lock-free операции — работает на уровне процессора
- Не блокирует поток — поток продолжает выполняться
- Только для простых типов — int, bool, указатели и т.п.
- Легче — минимальные затраты на синхронизацию
- Может использовать механизм compare-and-swap (CAS)
Сравнительная таблица
| Аспект | std::mutex | std::atomic |
|---|---|---|
| Тип синхронизации | Блокирующая (locked) | Lock-free |
| Производительность | Медленнее при отсутствии конфликтов | Быстрее |
| Сложность кода | Требует lock_guard, RAII | Простые операции |
| Применимость | Защита критических секций | Счётчики, флаги |
| Контекстные переключения | Да, создаёт overhead | Нет |
Когда использовать что?
Используй std::mutex, если:
- Нужно защитить сложный блок кода (несколько переменных)
- Требуется условная переменная (
std::condition_variable) - Критическая секция достаточно долгая
- Нужна гарантия fairness между потоками
Используй std::atomic, если:
- Работаешь с простыми типами (флаги, счётчики)
- Нужна максимальная производительность
- Операции очень быстрые
- Критическая секция минимальна
Практический пример конкурентного счётчика
#include <atomic>
#include <thread>
#include <vector>
std::atomic<long long> counter(0);
void worker() {
for (int i = 0; i < 100000; ++i) {
counter++; // lock-free инкремент
}
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(worker);
}
for (auto& t : threads) {
t.join();
}
// counter == 1000000
return 0;
}
Атомарные операции обеспечивают корректный результат без использования явных блокировок, что делает код быстрее и проще.
Гарантии памяти (Memory Ordering)
std::atomic поддерживает разные модели согласованности памяти через параметр std::memory_order:
memory_order_relaxed— минимальные гарантииmemory_order_acquire/release— для синхронизацииmemory_order_seq_cst— полная последовательная консистентность (по умолчанию)
std::atomic<bool> flag(false);
flag.store(true, std::memory_order_release);
if (flag.load(std::memory_order_acquire)) { /* ... */ }
Вывод: std::mutex — это инструмент для защиты сложных критических секций с гарантией fairness, а std::atomic — это высокопроизводительный примитив для работы с простыми типами без блокировок. Выбор зависит от сценария использования.