Спинлок против Семафора

Каковы основные различия между семафором и спин-блокировкой?

Когда мы будем использовать семафор поверх спин-блокировки?

10 ответов

Решение

Спинлок и семафор отличаются в основном четырьмя вещами:

1. Какие они есть
Спин-блокировка является одной из возможных реализаций блокировки, а именно той, которая реализуется посредством ожидания ожидания ("вращение"). Семафор - это обобщение блокировки (или, наоборот, блокировка - это особый случай семафора). Обычно, но не обязательно, спин-блокировки действительны только в рамках одного процесса, тогда как семафоры также могут использоваться для синхронизации между различными процессами.

Блокировка работает для взаимного исключения, то есть один поток за раз может получить блокировку и перейти к "критическому разделу" кода. Обычно это означает код, который изменяет некоторые данные, используемые несколькими потоками.
Семафор имеет счетчик и позволяет получать его одному или нескольким потокам, в зависимости от того, какое значение вы публикуете в нем, и (в некоторых реализациях) в зависимости от его максимально допустимого значения.

Поскольку блокировку можно считать частным случаем семафора с максимальным значением 1.

2. Что они делают
Как указано выше, спин-блокировка - это блокировка и, следовательно, механизм взаимного исключения (строго от 1 до 1). Он работает путем многократного запроса и / или изменения области памяти, обычно атомарным способом. Это означает, что получение спин-блокировки является "занятой" операцией, которая, возможно, сжигает циклы ЦП в течение длительного времени (возможно, навсегда!), В то время как она эффективно достигает "ничего".
Основным стимулом для такого подхода является тот факт, что переключение контекста имеет издержки, эквивалентные вращению несколько сотен (или, возможно, тысяч) раз, поэтому, если блокировка может быть получена путем записи нескольких циклов вращения, это в целом может быть очень более эффективным. Кроме того, для приложений реального времени может быть неприемлемо блокировать и ждать, пока планировщик вернется к ним в какое-то отдаленное время в будущем.

Семафор, напротив, либо не вращается вообще, либо вращается только в течение очень короткого времени (как оптимизация, чтобы избежать издержек системного вызова). Если семафор не может быть получен, он блокируется, отдавая процессорное время другому потоку, который готов к работе. Это, конечно, может означать, что перед повторным планированием потока проходит несколько миллисекунд, но если это не проблема (обычно это не так), то это может быть очень эффективным, консервативным для ЦП подходом.

3. Как они ведут себя при наличии заторов
Распространенным заблуждением является то, что спин-блокировки или алгоритмы без блокировок "обычно быстрее" или что они полезны только для "очень коротких задач" (в идеале, объект синхронизации не должен удерживаться дольше, чем это абсолютно необходимо).
Одно важное отличие заключается в том, как различные подходы ведут себя при наличии перегрузок.

Хорошо спроектированная система обычно имеет низкий уровень перегрузки или вообще не перегружен (это означает, что не все потоки пытаются получить блокировку в одно и то же время). Например, обычно не пишут код, который получает блокировку, затем загружают половину мегабайта сжатых zip-данных из сети, декодируют и анализируют данные и, наконец, изменяют общую ссылку (добавляют данные в контейнер и т. Д.). перед снятием блокировки. Вместо этого можно получить блокировку только с целью доступа к общему ресурсу.
Поскольку это означает, что вне критической секции значительно больше работы, чем внутри нее, естественно, вероятность того, что поток находится внутри критической секции, относительно мала, и, таким образом, несколько потоков одновременно борются за блокировку. Конечно, время от времени два потока будут пытаться получить блокировку одновременно (если это не может произойти, блокировка не понадобится!), Но это скорее исключение, чем правило в "здоровой" системе,

В таком случае спин-блокировка значительно превосходит семафор, потому что при отсутствии перегрузки блокировки накладные расходы на получение спин-блокировки составляют всего лишь дюжину циклов по сравнению с сотнями / тысячами циклов для переключения контекста или 10-20 миллионами циклов для потери остаток отрезка времени.

С другой стороны, из-за высокой загруженности или если блокировка удерживается в течение длительных периодов (иногда вы просто не можете с этим поделать!), Спин-блокировка сожжет безумное количество циклов ЦП, ничего не добившись.
В этом случае гораздо лучше выбрать семафор (или мьютекс), поскольку он позволяет другому потоку запускать полезные задачи в течение этого времени. Или, если ни у какого другого потока нет чего-то полезного, это позволяет операционной системе снизить нагрузку на ЦП и снизить потребление тепла / энергосбережение.

Кроме того, в одноядерной системе спин-блокировка будет весьма неэффективной при наличии перегрузки блокировки, поскольку вращающийся поток будет тратить свое полное время на ожидание изменения состояния, которое не может произойти (не до тех пор, пока не запланирован освобождающий поток, что не не происходит во время работы ожидающего потока!). Таким образом, при любом уровне разногласий получение блокировки в лучшем случае занимает около 1 1/2 временных интервалов (при условии, что освобождающий поток является следующим запланированным потоком), что не очень хорошее поведение.

4. Как они реализованы
Семафор в наше время обычно переносится sys_futex под Linux (опционально с спин-блокировкой, которая выходит после нескольких попыток).
Спин-блокировка обычно реализуется с использованием атомарных операций и без использования чего-либо, предоставляемого операционной системой. В прошлом это означало использование встроенных функций компилятора или непереносимых инструкций ассемблера. Между тем и C++11, и C11 имеют атомарные операции как часть языка, поэтому, помимо общей сложности написания корректно корректного кода без блокировки, теперь можно реализовать код без блокировки в полностью переносимом и (почти) безболезненно.

Проще говоря, семафор является "уступающим" объектом синхронизации, а спин-блокировка - "занятым". (Семафоры немного больше в том, что они синхронизируют несколько потоков, в отличие от мьютекса, охраны, монитора или критической секции, которая защищает область кода от одного потока).

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

обычно, если вы вращаетесь дольше, чем квант потока, вам следует использовать семафор.

Помимо того, что говорили Yoav Aviram и gbjbaanb, другим ключевым моментом было то, что вы никогда не будете использовать спин-блокировку на машине с одним процессором, тогда как семафор имел бы смысл на такой машине. В настоящее время вам часто бывает трудно найти машину без нескольких ядер, гиперпоточности или эквивалентной ей, но в тех случаях, когда у вас всего один процессор, вам следует использовать семафоры. (Полагаю, причина очевидна. Если один ЦП занят, ожидая, пока что-то еще освободит спин-блокировку, но работает на единственном ЦП, блокировка вряд ли будет снята, пока текущий процесс или поток не будет прерван O/S, что может занять некоторое время и ничего полезного не произойдет, пока не произойдет вытеснение.)

От драйверов устройств Linux от Rubinni

В отличие от семафоров, спин-блокировки могут использоваться в коде, который не может спать, например, обработчики прерываний

Я не эксперт по ядру, но вот несколько моментов:

Даже однопроцессорная машина может использовать спин-блокировки, если при компиляции ядра включено вытеснение ядра. Если выгрузка ядра отключена, спин-блокировка (возможно) расширяется до оператора void.

Кроме того, когда мы пытаемся сравнить семафор с Spin-блокировкой, я считаю, что семафор относится к тому, что используется в ядре, а не к тому, что используется для IPC (пользовательская область).

В основном, спин-блокировка должна использоваться, если критическая секция мала (меньше, чем накладные расходы сна / пробуждения), и критическая секция не вызывает ничего, что может спать! Семафор должен использоваться, если критическая секция больше и может спать.

Раман Чалотра.

Spinlock относится к реализации межпотоковой блокировки с использованием машинно-зависимых инструкций по сборке (таких как проверка и установка). Это называется спин-блокировкой, потому что поток просто ждет в цикле ("спины"), неоднократно проверяя, пока блокировка не станет доступной (занятое ожидание). Спин-блокировки используются в качестве замены мьютексов, которые представляют собой средства, предоставляемые операционными системами (а не процессором), поскольку спин-блокировки работают лучше, если они заблокированы на короткий период времени.

Semaphor - это средство, снабжаемое операционными системами для IPC, поэтому его основным назначением является межпроцессное взаимодействие. Будучи средством, обеспечиваемым операционной системой, его производительность не будет такой же хорошей, как у спин-блокировки для межкадровой блокировки (хотя и возможно). Семафоры лучше блокировать на более длительные периоды времени.

Тем не менее, реализация сплинлоков в сборке сложна, а не переносима.

Я хотел бы добавить свои наблюдения, более общие и не очень специфичные для Linux.

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

Да, если ваша архитектура памяти предлагает блокировку раздела памяти одним ядром / процессором, задерживая все остальные обращения, и если ваши процессоры предлагают тестирование и установку, вы можете реализовать семафор без спин-блокировки (но очень осторожно!).

Однако, поскольку разрабатываются простые / дешевые многоядерные системы (я работаю во встроенных системах), не все архитектуры памяти поддерживают такие функции многоядерных / многопроцессорных систем, только тестирование и установка или эквивалент. Тогда реализация может быть следующей:

  • получить спин-лок (занят ожиданием)
  • попробуйте приобрести семафор
  • снять спин-замок
  • если семафор не был успешно получен, приостановите текущий поток, пока семафор не будет освобожден; в противном случае переходите к критическому разделу.

Выпуск семафора должен быть реализован следующим образом:

  • приобрести спин-замок
  • выпустить семафор
  • снять спин-замок

Да, и для простых двоичных семафоров на уровне ОС можно было бы использовать только спин-блокировку в качестве замены. Но только если защищаемые участки кода действительно очень малы.

Как было сказано ранее, если и когда вы внедряете свою собственную ОС, будьте осторожны. Отладка таких ошибок - это весело (на мой взгляд, многие не разделяют), но в основном это очень утомительно и сложно.

В чем отличие спиновых замков от семафоров? Mark Biek:

Оба управляют ограниченным ресурсом. Сначала я опишу разницу между двоичным семафором (мьютексом) и спин-блокировкой.

Спин-блокировки выполняют занятое ожидание - т.е. он продолжает работать цикл:

while (try_acquire_resource ());...  
релиз();

Он выполняет очень легкую блокировку / разблокировку, но если поток блокировки будет вытеснен другим, который попытается получить доступ к тому же ресурсу, второй просто попытается получить ресурс, пока он не исчерпает свои кванты ЦП.
С другой стороны, мьютекс ведет себя как:

if (! try_lock ()) {
    add_to_waiting_queue ();
    Подождите();
}
...
process *p = get_next_process_from_waiting_queue ();
p->wakeUp ();

Следовательно, если поток попытается получить заблокированный ресурс, он будет приостановлен, пока не станет доступным для него. Блокировка / разблокировка намного тяжелее, но ожидание "бесплатно" и "честно".

Семафор - это блокировка, которую разрешено использовать несколько раз (известно из инициализации) - например, 3 потокам разрешено одновременно удерживать ресурс, но не более. Это используется, например, в проблеме производителя / потребителя или вообще в очередях:

Р (resources_sem)
resource = resources.pop ()...
resources.push (ресурсы)
В (resources_sem)

Разница между семафором, мьютексом и спин-блокировкой?

Блокировка в Linux

Spinlock используется тогда и только тогда, когда вы уверены, что ожидаемый результат произойдет очень скоро, до истечения времени выполнения вашего потока.

Пример: в модуле драйвера устройства драйвер записывает "0" в аппаратном регистре R0, и теперь ему нужно дождаться, пока этот регистр R0 станет 1. H/W читает R0 и выполняет некоторую работу и записывает "1" в R0. Это обычно быстро (в микросекундах). Теперь спиннинг намного лучше, чем идти спать и прерывается H/W. Конечно, во время вращения необходимо позаботиться о неисправности H/W!

Абсолютно нет причин для того, чтобы приложение пользователя вращалось. Это не имеет смысла. Вы собираетесь вращаться, чтобы произошло какое-то событие, и это событие должно быть завершено другим приложением пользовательского уровня, которое никогда не гарантируется в короткие сроки. Так что я вообще не буду крутиться в режиме пользователя. Я лучше спать () или mutexlock() или семафор блокировки () в режиме пользователя.

Блокировка спина может удерживаться только одним процессом, в то время как семафор может удерживаться одним или несколькими процессами. Спиновая блокировка ожидает, пока процесс снимет блокировку, а затем получит блокировку. Семафор находится в режиме ожидания блокировки, т.е. ждет и засыпает.

"Мьютекс" (или "блокировка взаимного исключения") - это сигнал, который два или более асинхронных процесса могут использовать для резервирования общего ресурса для исключительного использования. Первый процесс, который получает право собственности на "мьютекс", также получает право собственности на общий ресурс. Другие процессы должны дождаться, пока первый процесс освободит свое владение "мьютексом", прежде чем они могут попытаться получить его.

Самым распространенным примитивом блокировки в ядре является спин-блокировка. Спинлок - это очень простой замок с одним держателем. Если процесс пытается получить спин-блокировку и он недоступен, процесс будет пытаться (вращаться) до тех пор, пока не сможет получить блокировку. Эта простота создает небольшой и быстрый замок.

Другие вопросы по тегам