Спинлок и семафор отличаются в основном четырьмя вещами:
1. Какие они
spinlock является одной из возможных реализаций блокировки, а именно той, которая реализуется в режиме ожидания ожидания («вращение»). Семафор - это обобщение блокировки (или, наоборот, блокировка - это особый случай семафора). Обычно, , но не обязательно , спин-блокировки действительны только в пределах одного процесса, тогда как семафоры также могут использоваться для синхронизации между различными процессами.
Блокировка работает для взаимного исключения, то есть один поток за раз может получить блокировку и перейти к «критической секции» кода. Обычно это означает код, который изменяет некоторые данные, используемые несколькими потоками.
Семафор имеет счетчик и позволяет себе получать один или несколько потоков , в зависимости от того, какое значение вы публикуете в нем, и (в некоторых реализациях) в зависимости от того, какое его максимально допустимое значение значение.
Поскольку блокировку можно считать частным случаем семафора с максимальным значением 1.
2. Что они делают
Как указано выше, спин-блокировка - это блокировка и, следовательно, механизм взаимного исключения (строго 1 к 1). Он работает путем многократного запроса и / или изменения области памяти, обычно атомарным способом. Это означает, что получение спин-блокировки является «занятой» операцией, которая, возможно, сжигает циклы ЦП в течение длительного времени (возможно, навсегда!), В то время как эффективно достигает «ничего».
Основным стимулом для такого подхода является тот факт, что переключение контекста имеет издержки, эквивалентные вращению в несколько сотен (или, возможно, тысяч) раз, поэтому, если блокировка может быть получена путем записи нескольких циклов вращения, это в целом может быть более эффективным. Кроме того, для приложений реального времени может быть неприемлемо блокировать и ждать, пока планировщик вернется к ним в какое-то отдаленное время в будущем.
Семафор, напротив, либо не вращается вообще, либо вращается только в течение очень короткого времени (как оптимизация, чтобы избежать издержек системного вызова). Если семафор не может быть получен, он блокируется, отдавая процессорное время другому потоку, который готов к работе. Это, конечно, может означать, что перед повторным планированием потока проходит несколько миллисекунд, но если это не проблема (обычно это не так), то это может быть очень эффективным, консервативным для ЦП подходом.
3. Как они ведут себя при наличии скопления
Распространенным заблуждением является то, что спин-блокировки или алгоритмы без блокировок «обычно быстрее» или что они полезны только для «очень коротких задач» (в идеале, объект синхронизации не должен удерживаться дольше, чем это абсолютно необходимо).
Одно важное различие заключается в том, как различные подходы ведут себя при наличии скопления .
Хорошо спроектированная система обычно имеет низкий уровень перегрузки или вообще не перегружен (это означает, что не все потоки пытаются получить блокировку в одно и то же время). Например, обычно не записывают код, который получает блокировку, затем загружает из сети половину мегабайта сжатых zip-данных, декодирует и анализирует данные и, наконец, изменяет общую ссылку (добавляет данные контейнер и т. д.) до снятия блокировки. Вместо этого можно получить блокировку только для доступа к общему ресурсу .
Поскольку это означает, что вне критической секции значительно больше работы, чем внутри нее, естественно, вероятность того, что поток находится внутри критической секции, относительно низка, и, следовательно, несколько потоков одновременно борются за блокировку. Конечно, время от времени два потока будут пытаться получить блокировку одновременно (если это не может случиться, вам не понадобится блокировка!), Но это скорее исключение, чем правило в "здоровой" системе.
В таком случае спин-блокировка значительно превосходит семафор, потому что если нет перегрузки блокировки, накладные расходы на получение спин-блокировки составляют всего дюжину циклов по сравнению с сотнями / тысячами циклов для контекста переключение или 10-20 миллионов циклов для потери оставшейся части временного интервала.
С другой стороны, учитывая высокую загруженность или если блокировка удерживается в течение длительных периодов (иногда вы просто не можете с этим поделать!), Спин-блокировка сожжет безумное количество циклов ЦП, ничего не добившись.
В этом случае гораздо лучше выбрать семафор (или мьютекс), поскольку он позволяет другому потоку запускать полезных задач в течение этого времени. Или, если ни у какого другого потока нет чего-то полезного, это позволяет операционной системе снизить нагрузку на ЦП и снизить потребление тепла / энергосбережение.
Кроме того, в одноядерной системе спин-блокировка будет весьма неэффективной при наличии перегрузки блокировки, поскольку вращающаяся нить будет тратить свое полное время на ожидание изменения состояния, которое не может произойти (не до тех пор, пока не будет запланирован выпуск потока). , чего не происходит во время работы ожидающего потока!). Следовательно, учитывая любое количество разногласий, получение блокировки занимает около 1 1/2 временных интервалов в лучшем случае (при условии, что освобождающий поток является следующим запланированным потоком), что не очень хорошее поведение.
4. Как они реализованы
Семафор в настоящее время обычно переносит sys_futex
под Linux (опционально с спин-блокировкой, которая завершается после нескольких попыток).
Спин-блокировка обычно реализуется с использованием атомарных операций и без использования чего-либо, предоставляемого операционной системой. В прошлом это означало использование встроенных функций компилятора или непереносимых инструкций ассемблера. Между тем и C ++ 11, и C11 имеют атомарные операции как часть языка, поэтому, помимо общей сложности написания корректно корректного кода без блокировки, теперь можно реализовать код без блокировки в полностью переносимом и (почти) безболезненно.