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

В чем разница между std::mutex и std::atomic?

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

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

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

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

Разница между 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::mutexstd::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 — это высокопроизводительный примитив для работы с простыми типами без блокировок. Выбор зависит от сценария использования.