Ответ
Mutex — блокирующий примитив синхронизации. Если поток пытается захватить уже занятый мьютекс, он переводится в состояние ожидания (sleep), освобождая процессорное время. Это эффективно для критических секций, выполнение которых занимает относительно долгое время, так как избегает бесполезного потребления CPU.
Spinlock — неблокирующий примитив, реализующий активное ожидание (busy-waiting). Поток в цикле проверяет, освободилась ли блокировка, не отдавая процессор. Это оправдано для очень коротких критических секций (например, несколько инструкций), где накладные расходы на переключение контекста и усыпление потока могут превысить время самого ожидания.
Ключевые отличия:
- Поведение при занятости: Mutex — sleep, Spinlock — busy-wait.
- Накладные расходы: Mutex имеет большие overhead на переключение контекста, Spinlock — минимальные, но загружает ядро ЦП на 100%.
- Использование: Mutex для операций > ~1-2 мкс, Spinlock — для сверхкоротких операций.
Пример реализации простого Spinlock на C++ с использованием std::atomic_flag:
#include <atomic>
class SpinLock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock() {
while (flag.test_and_set(std::memory_order_acquire)) {
// Подсказка процессору для активного ожидания (может снизить энергопотребление)
__builtin_ia32_pause(); // Для x86 / __asm__ volatile("pause" ::: "memory");
}
}
void unlock() {
flag.clear(std::memory_order_release);
}
};
// Использование
SpinLock slock;
slock.lock();
// Критическая секция
slock.unlock();
На практике в C++ чаще используют std::mutex (реализация которого внутри может кратковременно использовать спин-луп) или std::atomic для lock-free алгоритмов.