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

В чем разница между Mutex и Spinlock?

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

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

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

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

Разница между Mutex и Spinlock: разные стратегии ожидания

Оба механизма синхронизации служат для защиты критических секций, но используют кардинально разные подходы к ожиданию блокировки. Выбор между ними зависит от времени ожидания и типа системы.

Что такое Mutex (Mutual Exclusion)

Mutex — блокировка, которая переводит поток в сонное состояние, если ресурс занят.

#include <mutex>
#include <thread>

std::mutex m;
int shared_counter = 0;

void increment() {
    std::lock_guard<std::mutex> lock(m);  // Заблокировать
    shared_counter++;  // Критическая секция
}  // Разблокировать (lock_guard деструктор)

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

  1. Поток пытается захватить mutex
  2. Если свободен — захватывает и продолжает
  3. Если занят — поток уходит в очередь и переводится в режим ожидания (sleep)
  4. ОС пробуждает поток когда mutex освобождается

Что такое Spinlock

Spinlock — блокировка, которая активно ожидает в цикле (busy-waiting).

struct Spinlock {
    std::atomic<bool> flag = false;
    
    void lock() {
        // Крутимся в цикле пока не захватим
        while (flag.exchange(true, std::memory_order_acquire)) {
            // Активное ожидание - поток НЕ спит!
        }
    }
    
    void unlock() {
        flag.store(false, std::memory_order_release);
    }
};

Spinlock spin_lock;

void critical_section() {
    spin_lock.lock();
    // ... работа ...
    spin_lock.unlock();
}

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

  1. Поток пытается захватить spinlock
  2. Если свободен — захватывает
  3. Если занят — поток остаётся активным и постоянно проверяет флаг
  4. Это потребляет CPU, но даёт быстрый доступ когда ресурс освобождается

Сравнение основных характеристик

Поведение при блокировке:

  • Mutex: поток спит, не потребляет CPU ✓
  • Spinlock: поток активно крутится, потребляет CPU ✗

Задержка захвата после освобождения:

  • Mutex: может быть задержка на пробуждение (микросекунды-миллисекунды)
  • Spinlock: минимальная задержка (наносекунды)

Потребление ресурсов:

  • Mutex: низкое при длительном ожидании
  • Spinlock: высокое (топит CPU)

Справедливость (fairness):

  • Mutex: гарантирует FIFO очередь
  • Spinlock: непредсказуемо, может "прыгнуть в очередь"

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

1. Ожидание может быть долгим

std::mutex io_lock;

void read_from_disk() {
    std::lock_guard<std::mutex> lock(io_lock);
    // I/O операция может занять миллисекунды
    // Нет смысла жечь CPU в spinlock
    disk.read();
}

2. Много потоков конкурируют

std::mutex m;

// 100 потоков ждут одного mutex
// Spinlock было бы disaster - 99% CPU для ничего
for (int i = 0; i < 100; i++) {
    std::lock_guard<std::mutex> lock(m);
    // работа
}

3. Переменное время ожидания

std::mutex cache_lock;

void access_cache() {
    std::lock_guard<std::mutex> lock(cache_lock);
    // Иногда 1 микросекунда, иногда 100 миллисекунд
    // Mutex справляется лучше
}

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

1. Критическая секция очень короткая

Spinlock ref_count_lock;
int ref_count = 0;

void increment_ref() {
    spin_lock.lock();
    ref_count++;  // 1-2 инструкции - микросекунда
    spin_lock.unlock();
    // Переключение контекста дороже, чем spinning
}

2. Ожидание почти всегда короткое

Spinlock update_lock;

void update_counter() {
    spin_lock.lock();
    // Обычно занято < 100 наносекунд
    counter++;
    spin_lock.unlock();
}

3. System programming / Real-time

// В kernel code или real-time системах
// где нельзя позволить себе context switch
Spinlock kernel_lock;

void kernel_critical() {
    spin_lock.lock();
    // Должно быть очень быстро
    // и предсказуемо по времени
    spin_lock.unlock();
}

4. Системы с малым числом потоков

// 2-4 потока на 4 ядрах
// Каждый может крутиться на своём ядре
// Нет проблемы "голодного" процесса
Spinlock lock;

Практические примеры

Плохо: Spinlock на I/O операции

Spinlock bad_lock;  // ❌ Криминально плохо

void read_file() {
    bad_lock.lock();
    // Ожидание I/O может быть 10ms
    // Поток жрёт 100% CPU впустую
    file.read();
    bad_lock.unlock();
}

Хорошо: Spinlock на микроскопической операции

Spinlock good_lock;  // ✓ Имеет смысл
std::atomic<int> counter = 0;

void increment() {
    good_lock.lock();
    counter++;  // ~5 наносекунд
    good_lock.unlock();
    // Переключение контекста дороже
}

C++ стандартные инструменты

Для Mutex:

#include <mutex>

std::mutex m;
std::lock_guard<std::mutex> lock(m);          // RAII автоматик
std::unique_lock<std::mutex> ulock(m);        // С возможностью разблокировать
std::scoped_lock multi_lock(m1, m2, m3);      // Несколько mutex одновременно

Для Spinlock:

#include <atomic>
#include <thread>

class Spinlock {
private:
    std::atomic<bool> locked = false;

public:
    void lock() {
        while (locked.exchange(true, std::memory_order_acquire)) {
            std::this_thread::yield();  // Подсказка ОС: дай ход другому потоку
        }
    }
    
    void unlock() {
        locked.store(false, std::memory_order_release);
    }
};

Гибридный подход: Hybrid Lock

Можно комбинировать оба подхода:

class HybridLock {
private:
    std::atomic<int> state = 0;
    std::mutex fallback;

public:
    void lock() {
        // Сначала попробуем spinlock
        for (int i = 0; i < 100; i++) {
            if (state.compare_exchange_weak(0, 1)) {
                return;  // Захватили быстро
            }
            std::this_thread::yield();
        }
        // Если не удалось за 100 итераций - mutex
        fallback.lock();
    }
    
    void unlock() {
        state.store(0);
    }
};

Правило для выбора

Время в критической секции < 10 микросекунд? → Spinlock
Время в критической секции > 100 микросекунд? → Mutex
10-100 микросекунд? → Смотря на ситуацию, может быть гибридный

Вывод

  • Mutex — универсальный выбор для большинства случаев
  • Spinlock — для очень коротких критических секций
  • По умолчанию начни с Mutex — это безопаснее для CPU
  • Профилируй перед оптимизацией, не гадай
  • В многопоточных приложениях Mutex почти всегда правильный выбор
В чем разница между Mutex и Spinlock? | PrepBro