Ответ
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 алгоритмов.
Ответ 18+ 🔞
Давай разберем эту тему, чтобы не было как в том анекдоте, где один чувак пытается открыть додик, а другой его уже изнутри на спинлоке держит. Полный ёперный театр.
Представь, что у тебя есть один унитаз на весь офис — это общий ресурс. Мьютекс — это как взять талончик и спокойно присесть на диванчик с телефоном, пока предыдущий посетитель делает свои дела. Ты не тратишь силы, просто ждешь своей очереди. Потом тебя позовут. Это эффективно, если там кто-то надолго засел, с планшетом и кофе. Твои овердохуища накладных расходов — это переключение контекста, усыпление и пробуждение потока. Зато процессор свободен.
А теперь спинлок. Это когда ты не берешь талончик, а стоишь вплотную к двери, дергаешь ручку каждую миллисекунду и орешь: «Сколько можно там сидеть, я ща обоссусь!». Ты не спишь, ты в активном ожидании, и твое ядро процессора загружено на все сто, как будто ты что-то делаешь, хотя по факту просто бздишь на месте. Но! Если чувак внутри только пукнуть зашел и сразу выйдет, то твои затраты на дерганье ручки будут меньше, чем беготня за талончиком и обратно.
Ключевые отличия, если по-простому:
- Занято? Mutex: «Окей, посплю». Spinlock: «Нет, бля, буду дёргать, пока не откроют!».
- Цена вопроса: Mutex — дорогое переключение контекста. Spinlock — дешёвый цикл, но процессор жрёт как печь.
- Когда что юзать: Mutex — когда операция в критической секции долгая (больше пары микросекунд). Spinlock — только для суперкоротких действий, буквально пару инструкций, где усыплять поток нахуя не нужно.
Вот, смотри, пиздопроебибна реализация спинлока на atomic_flag. Главное — не забыть паузу для процессора, а то он совсем с ума сойдёт от такого цикла.
#include <atomic>
class SpinLock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock() {
// Пока флаг уже установлен кем-то (test_and_set возвращает true), крутимся
while (flag.test_and_set(std::memory_order_acquire)) {
// Подсказка процессору: "Чувак, расслабься, я тут подожду"
__builtin_ia32_pause(); // Для x86
}
}
void unlock() {
flag.clear(std::memory_order_release);
}
};
// Использование
SpinLock slock;
slock.lock();
// Твоя сверхбыстрая критическая секция. Только зайди-выйди.
slock.unlock();
На деле, конечно, в C++ чаще хуй с горы — берут std::mutex и не парятся. А умные его реализации внутри сами решают, когда немного покрутиться в спинлупе, а когда уже усыплять. Но понимать разницу — это, чувак, основа, чтобы не написать какую-нибудь хитрую жопу, которая будет бензин хавать просто так.