Каковы основные различия между семафором и спин-блокировкой?
Когда мы будем использовать семафор вместо спин-блокировки?
Каковы основные различия между семафором и спин-блокировкой?
Когда мы будем использовать семафор вместо спин-блокировки?
Спин-блокировка и семафор отличаются в основном четырьмя вещами:
<сильный>1. Что это такое
Вращающаяся блокировка – это одна из возможных реализаций блокировки, а именно та, которая реализована с помощью ожидания занятости ("вращения"). Семафор — это обобщение замка (или, наоборот, замок — частный случай семафора). Обычно, но не обязательно, спин-блокировки допустимы только внутри одного процесса, тогда как семафоры также могут использоваться для синхронизации между различными процессами.
Блокировка работает для взаимного исключения, то есть один поток за раз может получить блокировку и продолжить работу с "критическим разделом" кода. Обычно это означает код, который изменяет некоторые данные, совместно используемые несколькими потоками.
Семафор имеет счетчик и может быть получен одним или несколькими потоками, в зависимости от какое значение вы отправляете в него и (в некоторых реализациях) в зависимости от его максимально допустимого значения.
До сих пор можно рассматривать блокировку как частный случай семафора с максимальным значением 1.
<сильный>2. Что они делают
Как сказано выше, спин-блокировка — это блокировка и, следовательно, механизм взаимного исключения (строго 1 к 1). Он работает путем многократного запроса и/или изменения области памяти, обычно атомарным образом. Это означает, что получение спин-блокировки является «занятой» операцией, которая может сжигать циклы ЦП в течение длительного времени (возможно, навсегда!), в то время как фактически «ничего не достигается».
Основным стимулом для такого подхода является тот факт, что контекст switch имеет накладные расходы, эквивалентные нескольким сотням (или, может быть, тысячам) вращений, поэтому, если блокировку можно получить, прожигая несколько циклов вращения, это в целом может быть более эффективным. Кроме того, для приложений реального времени может быть неприемлемо блокировать и ждать, пока планировщик вернется к ним в какое-то отдаленное время в будущем.
Семафор, напротив, либо вообще не вращается, либо вращается только в течение очень короткого времени (как оптимизация, позволяющая избежать накладных расходов на системные вызовы). Если семафор не может быть получен, он блокируется, уступая процессорное время другому потоку, готовому к запуску. Это, конечно, может означать, что пройдет несколько миллисекунд, прежде чем ваш поток будет снова запланирован, но если это не проблема (обычно это не так), то это может быть очень эффективным подходом, щадящим ЦП.
<сильный>3. Как они ведут себя в условиях перегрузки
Распространено заблуждение, что спин-блокировки или алгоритмы без блокировок «в целом быстрее» или что они полезны только для «очень коротких задач» (в идеале, без объекта синхронизации). следует удерживать дольше, чем это абсолютно необходимо).
Одно важное различие заключается в том, как различные подходы ведут себя в условиях перегрузки.
Хорошо спроектированная система обычно имеет низкую перегрузку или ее отсутствие (это означает, что не все потоки пытаются получить блокировку в одно и то же время). Например, обычно не нужно писать код, который получает блокировку, затем загружает полмегабайта сжатых данных из сети, декодирует и анализирует данные и, наконец, изменяет общую ссылку (добавляет данные к контейнеру и т. д.), прежде чем снять блокировку. Вместо этого можно получить блокировку только с целью доступа к общему ресурсу.
Поскольку это означает, что за пределами критической секции выполняется значительно больше работы, чем внутри нее, естественно, вероятность того, что поток находиться внутри критической секции относительно мало, и, таким образом, за блокировку одновременно борется несколько потоков. Конечно, время от времени два потока будут пытаться получить блокировку одновременно (если бы это не могло случиться, вам не нужна была бы блокировка!), но это скорее исключение, чем правила в «здоровой» системе.
В таком случае спин-блокировка значительно превосходит семафор, потому что, если нет перегрузки блокировки, накладные расходы на получение спин-блокировки составляют всего дюжину циклов по сравнению с сотнями/тысячами циклов для переключения контекста или 10-20 миллионов циклов за потерю остатка кванта времени.
С другой стороны, при высокой перегрузке или если блокировка удерживается в течение длительного времени (иногда вы просто ничего не можете поделать!), спин-блокировка будет сжигать безумное количество циклов ЦП, чтобы ничего не добиться.
Семафор ( или мьютекс) в этом случае является гораздо лучшим выбором, так как позволяет другому потоку выполнять полезные задачи в это время. Или, если никакой другой поток не может сделать что-то полезное, он позволяет операционной системе снизить нагрузку на ЦП и уменьшить тепло/сберегать энергию.
Кроме того, в одноядерной системе спин-блокировка будет совершенно неэффективной при перегрузке блокировок, поскольку вращающийся поток будет тратить все свое время на ожидание изменения состояния, которое не может произойти (пока не будет запланировано освобождение потока, что < em>не происходит, пока выполняется ожидающий поток!). Таким образом, при любом количестве конфликтов получение блокировки в лучшем случае занимает около 1 1/2 кванта времени (при условии, что освобождающий поток является следующим по расписанию), что не очень хорошо.
<сильный>4. Как они реализованы
В настоящее время семафор обычно обертывает sys_futex
под Linux (необязательно с спин-блокировкой, которая выходит после нескольких попыток).
Спин-блокировка обычно реализуется с использованием атомарных операций и без использования чего-либо. обеспечивается операционной системой. В прошлом это означало использование либо встроенных функций компилятора, либо инструкций непереносимого ассемблера. Между тем как C++11, так и C11 имеют атомарные операции как часть языка, так что помимо общей сложности написания доказуемо корректного кода без блокировок, теперь можно реализовать код без блокировок полностью переносимым и (почти) безболезненный способ.
spin_trylock
, который немедленно возвращается с кодом ошибки, если блокировка не может быть получена. Спин‑блокировка не всегда бывает такой жесткой. Но использование spin_trylock
требует, чтобы приложение было правильно спроектировано таким образом (вероятно, очередь ожидающих операций, а здесь выбор следующей, оставляя фактическую в очереди).
- person Hibou57; 30.07.2013
очень просто, семафор - это "уступающий" объект синхронизации, спин-блокировка - это "ожидание занятости". (в семафорах есть нечто большее, поскольку они синхронизируют несколько потоков, в отличие от мьютекса, охраны, монитора или критической секции, которые защищают область кода от одного потока)
Вы бы использовали семафор в большем количестве случаев, но используйте спин-блокировку, когда вы собираетесь блокироваться на очень короткое время - блокировка требует затрат, особенно если вы блокируете много. В таких случаях может быть более эффективным ненадолго заблокировать, ожидая разблокировки защищенного ресурса. Очевидно, что при слишком долгом вращении производительность снижается.
обычно, если вы вращаетесь дольше, чем квант потока, вам следует использовать семафор.
Помимо того, что сказали Йоав Авирам и gbjbaanb, другим ключевым моментом было то, что вы никогда не будете использовать спин-блокировку на машине с одним процессором, тогда как семафор будет иметь смысл на такой машине. В настоящее время вам часто трудно найти машину без нескольких ядер, гиперпоточности или чего-то подобного, но в обстоятельствах, когда у вас есть только один процессор, вам следует использовать семафоры. (Я полагаю, что причина очевидна. Если один ЦП занят ожиданием снятия спин-блокировки чем-то другим, но он работает на единственном ЦП, блокировка вряд ли будет снята до тех пор, пока текущий процесс или поток не будет вытеснен ОС, что может занять некоторое время, и ничего полезного не произойдет, пока не произойдет вытеснение.)
Из драйверов устройств Linux от Rubinni
В отличие от семафоров, спин-блокировки могут использоваться в коде, который не может спать, например, в обработчиках прерываний.
Я не эксперт по ядру, но вот несколько моментов:
Даже однопроцессорная машина может использовать спин-блокировки, если во время компиляции ядра включено вытеснение ядра. Если вытеснение ядра отключено, спин-блокировка (возможно) заменяется оператором void.
Кроме того, когда мы пытаемся сравнить семафор и спин-блокировку, я считаю, что семафор относится к тому, который используется в ядре, а НЕ к тому, который используется для IPC (пользовательская среда).
По сути, спин-блокировка должна использоваться, если критическая секция мала (меньше, чем накладные расходы на сон/пробуждение) и критическая секция не вызывает ничего, что может спать! Семафор следует использовать, если критическая секция больше и может спать.
Раман Чалотра.
Spinlock относится к реализации блокировки между потоками с использованием машинно-зависимых инструкций по сборке (таких как test-and-set). Это называется спин-блокировкой, потому что поток просто ждет в цикле («вращается»), многократно проверяя, пока блокировка не станет доступной (ожидание занятости). Спин-блокировки используются вместо мьютексов, которые являются средством, предоставляемым операционными системами (а не ЦП), потому что спин-блокировки работают лучше, если они заблокированы на короткий период времени.
Семафор — это средство, предоставляемое операционными системами для IPC, поэтому его основной целью является взаимодействие между процессами. Будучи средством, предоставляемым операционной системой, его производительность будет не такой хорошей, как у спин-блокировки для блокировки между головками (хотя это возможно). Семафоры лучше блокировать на более длительные периоды времени.
Тем не менее, реализация splinlocks в сборке сложна и не переносима.
Я хотел бы добавить свои наблюдения, более общие и не очень специфичные для Linux.
В зависимости от архитектуры памяти и возможностей процессора вам может понадобиться спин-блокировка для реализации семафора в многоядерной или многопроцессорной системе, потому что в таких системах может возникнуть состояние гонки, когда два или более потока/процесса хотят приобрести семафор.
Да, если ваша архитектура памяти предлагает блокировку раздела памяти одним ядром/процессором с задержкой всех других доступов, и если ваши процессоры предлагают проверку и установку, вы можете реализовать семафор без спин-блокировки (но очень осторожно! ).
Однако, поскольку разрабатываются простые / дешевые многоядерные системы (я работаю во встроенных системах), не все архитектуры памяти поддерживают такие многоядерные / многопроцессорные функции, только тестовые и устанавливаемые или эквивалентные. Тогда реализация может быть следующей:
Освобождение семафора должно быть реализовано следующим образом:
Да и для простых бинарных семафоров на уровне ОС можно было бы использовать в качестве замены только спин-блокировку. Но только в том случае, если защищаемые участки кода действительно очень малы.
Как было сказано ранее, если и когда вы внедряете свою собственную ОС, будьте осторожны. Отладка таких ошибок забавна (мое мнение, разделяемое не многими), но в большинстве случаев очень утомительна и трудна.
«Мьютекс» (или «блокировка взаимного исключения») — это сигнал, который два или более асинхронных процесса могут использовать для резервирования общего ресурса для монопольного использования. Первый процесс, получивший право собственности на «мьютекс», также получает право собственности на общий ресурс. Другие процессы должны дождаться, пока первый процесс освободит свое право собственности на «мьютекс», прежде чем они смогут попытаться его получить.
Наиболее распространенным блокирующим примитивом в ядре является спин-блокировка. Спинлок — это очень простой замок с одним держателем. Если процесс пытается получить спин-блокировку, а она недоступна, процесс будет продолжать попытки (вращаться), пока не сможет получить блокировку. Эта простота создает небольшой и быстрый замок.
Spinlock используется тогда и только тогда, когда вы уверены, что ожидаемый результат произойдет очень скоро, прежде чем истечет время среза выполнения вашего потока.
Пример: в модуле драйвера устройства драйвер записывает «0» в аппаратный регистр R0, и теперь ему нужно дождаться, пока этот регистр R0 станет 1. Аппаратное обеспечение считывает R0 и выполняет некоторую работу и записывает «1» в R0. Как правило, это быстро (в микросекундах). Теперь крутиться намного лучше, чем ложиться спать и прерываться на H/W. Конечно, при вращении нужно следить за состоянием отказа H/W!
Пользовательскому приложению абсолютно нет причин вращаться. Это не имеет смысла. Вы собираетесь вращаться, чтобы произошло какое-то событие, и это событие должно быть завершено другим приложением пользовательского уровня, что никогда не гарантируется в короткие сроки. Так вот, я вообще не буду крутить в пользовательском режиме. Мне лучше использовать sleep(), mutexlock() или semaphore lock() в пользовательском режиме.
Из в чем разница между спин-блокировками и семафоры? автор Мацей Пехотка:
Оба управляют ограниченным ресурсом. Сначала я опишу разницу между бинарным семафором (мьютексом) и спин-блокировкой.
Спин-блокировки выполняют ожидание занятости, т. е. продолжают выполнять цикл:
while (try_acquire_resource ()); ... release();Он выполняет очень легкую блокировку/разблокировку, но если блокирующий поток будет вытеснен другим, который попытается получить доступ к тому же ресурсу, второй будет просто пытаться получить ресурс, пока не закончатся кванты ЦП.
С другой стороны, поведение мьютекса больше похоже на:if (!try_lock()) { add_to_waiting_queue (); wait(); } ... process *p = get_next_process_from_waiting_queue (); p->wakeUp ();Следовательно, если поток попытается получить заблокированный ресурс, он будет приостановлен до тех пор, пока он не станет для него доступным. Блокировка/разблокировка намного сложнее, но ожидание «бесплатно» и «честно».
Семафор — это блокировка, которую можно использовать несколько раз (известно при инициализации) количество раз - например, 3 потока могут одновременно удерживать ресурс, но не более. Он используется, например, в задаче производителя/потребителя или вообще в очередях:
P(resources_sem) resource = resources.pop() ... resources.push(resources) V(resources_sem)
Разница между семафором, мьютексом и спин-блокировкой?
спин-блокировка может удерживаться только одним процессом, в то время как семафор может удерживаться одним или несколькими процессами. Спин-блокировка ждет, пока процесс освободит блокировку, а затем получит блокировку. Семафор находится в спящем режиме, т.е. ждет и переходит в спящий режим.