Когда следует использовать спин-блокировку вместо мьютекса? - PullRequest
264 голосов
/ 03 мая 2011

Я думаю, что оба выполняют одну и ту же работу. Как вы решаете, какой из них использовать для синхронизации?

Ответы [ 6 ]

670 голосов
/ 03 мая 2011

Теория

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

Проблема

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

Решение

Использование спин-блокировок в одноядерной / однопроцессорной системе, как правило, не имеет смысла, поскольку, пока опрос с использованием спин-блокировки блокирует единственное доступное ядро ​​ЦП, никакой другой поток не может работать, а другой поток не может работать, блокировка не будет разблокирована либо. Таким образом, спин-блокировка тратит впустую только процессорное время в этих системах без реальной выгоды. Если вместо этого поток был переведен в спящий режим, другой поток мог бы запуститься сразу, возможно, разблокировав блокировку и затем позволив первому потоку продолжить обработку, как только он снова проснулся.

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

Практика

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

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

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

Резюме

Если вы сомневаетесь, используйте мьютексы, они, как правило, являются лучшим выбором, и большинство современных систем позволяют им в течение очень короткого промежутка времени, если это кажется полезным.Использование спин-блокировок иногда может улучшить производительность, но только при определенных условиях, и тот факт, что вы сомневаетесь, скорее говорит мне о том, что вы не работаете над каким-либо проектом, в котором спин-блокировка может быть полезной.Вы можете рассмотреть возможность использования своего собственного «объекта блокировки», который может использовать внутреннюю спин-блокировку или мьютекс (например, это поведение может быть настроено при создании такого объекта), изначально использовать мьютексы везде, и если вы думаете, что использование спин-блокировки где-то может действительнопомогите, попробуйте и сравните результаты (например, с помощью профилировщика), но обязательно протестируйте оба варианта, одноядерную и многоядерную систему, прежде чем делать выводы (и, возможно, разные операционные системы, если ваш кодбудет кроссплатформенным).

Обновление: предупреждение для iOS

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

Проблема выглядит следующим образом: Ваш код получает спин-блокировку в потоке класса с низким уровнем prio, и пока он находится в середине этой блокировки, квант времени превышен, и поток прекращает работу.Единственный способ, которым эта спин-блокировка может быть освобождена снова, - это если поток с низким уровнем prio снова получает процессорное время, но это не гарантируется.У вас может быть несколько потоков с высоким уровнем prio, которые постоянно хотят работать, и планировщик задач всегда будет определять их приоритетность.Один из них может натолкнуться на спин-блокировку и попытаться получить ее, что, конечно, невозможно, и система заставит ее работать.Проблема в том, что поток, который дал, сразу же доступен для запуска снова!Имея более высокий приоритет, чем поток, удерживающий блокировку, поток, удерживающий блокировку, не имеет шансов получить время выполнения процессора.Либо другой поток получит время выполнения, либо поток, который только что выдался.

Почему эта проблема не возникает с мьютексами? Когда высокоприоритетный поток не может получить мьютекс, он не даст, он может немного вращаться, но в конечном итоге будет отправлен в спящий режим. Спящая нить недоступна для запуска, пока она не будет разбужена событием, например, событие, такое как мьютекс, был разблокирован, которого он ждал. Apple знает об этой проблеме и в результате устарела OSSpinLock. Новый замок называется os_unfair_lock. Эта блокировка позволяет избежать ситуации, упомянутой выше, поскольку она осведомлена о различных классах приоритетов потоков. Если вы уверены, что использование spinlocks - хорошая идея в вашем проекте iOS, используйте эту. Держитесь подальше от OSSpinLock! И ни при каких обстоятельствах не реализуйте свои собственные спин-блокировки в iOS! Если сомневаетесь, используйте мьютекс! MacOS не подвержен этой проблеме, поскольку имеет другой планировщик потоков, который не позволяет ни одному потоку (даже потокам с низким приоритетом) «работать всухую» на процессоре, но при этом может возникнуть такая же ситуация, которая затем приведет к очень плохому результату. производительность, таким образом, OSSpinLock устарела и в macOS.

7 голосов
/ 16 мая 2011

Продолжая предложение Меки, эта статья pthread mutex против pthread spinlock в блоге Александра Сэндлера, Алекс в Linux показывает, как можно реализовать spinlock & mutexes для проверки поведения с помощью #ifdef.

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

6 голосов
/ 16 апреля 2015

Ответ Меки вполне приличен. Однако на одном процессоре использование спин-блокировки может иметь смысл, когда задача ожидает блокировки, которая будет предоставлена ​​программой обработки прерываний. Прерывание передало бы управление ISR, что подготовило бы ресурс для использования ожидающей задачей. Это закончится снятием блокировки, прежде чем вернуть управление прерванной задаче. Вращающаяся задача найдет спинлок доступным и продолжит работу.

6 голосов
/ 22 июля 2013

Обратите также внимание, что в определенных средах и условиях (таких как запуск в Windows на уровне диспетчеризации> = УРОВЕНЬ ОТПРАВЛЕНИЯ) вы не можете использовать мьютекс, а скорее спин-блокировку. На unix - тоже самое.

Вот эквивалентный вопрос на сайте unix сайта конкурента stackexchange: https://unix.stackexchange.com/questions/5107/why-are-spin-locks-good-choices-in-linux-kernel-design-instead-of-something-more

Информация о диспетчеризации в системах Windows: http://download.microsoft.com/download/e/b/a/eba1050f-a31d-436b-9281-92cdfeae4b45/IRQL_thread.doc

3 голосов
/ 08 ноября 2013

Механизмы синхронизации Spinlock и Mutex сегодня очень распространены.

Давайте сначала подумаем о Спинлоке.

По сути, это активное ожидание, что означает, что мы должны ждать, пока не будет снята указанная блокировка, прежде чем мы сможем перейти к следующему действию. Концептуально очень просто, при этом его реализации не по делу. Например: если блокировка не была снята, то поток был выгружен и перешел в состояние сна, должны ли мы справиться с этим? Как бороться с блокировками синхронизации, когда два потока одновременно запрашивают доступ?

Как правило, наиболее интуитивной идеей является работа с синхронизацией через переменную для защиты критической секции. Концепция Mutex похожа, но они все же разные. Фокус на: использование процессора. Spinlock потребляет процессорное время для ожидания выполнения действия, и поэтому мы можем суммировать разницу между ними:

В однородных многоядерных средах, если время, затрачиваемое на критическую секцию, мало, чем использовать Spinlock, потому что мы можем сократить время переключения контекста. (Сравнение одноядерных не важно, потому что некоторые системы реализации Spinlock в середине коммутатора)

В Windows использование Spinlock обновит поток до DISPATCH_LEVEL, что в некоторых случаях может быть запрещено, поэтому на этот раз нам пришлось использовать Mutex (APC_LEVEL).

0 голосов
/ 26 сентября 2016

Использование спин-блокировок в одноядерной / однопроцессорной системе, как правило, не имеет смысла, поскольку, пока опрос с использованием спин-блокировки блокирует единственное доступное ядро ​​ЦП, никакой другой поток не может работать, а другой поток не может работать, блокировка не будет разблокирована либо. IOW, спин-блокировка тратит впустую только процессорное время в этих системах без реальной выгоды

Это неправильно. При использовании спин-блокировок в однопроцессорных системах потери процессоров не теряются, потому что, как только процесс получает спин-блокировку, вытеснение отключается, поэтому больше никто не вращается! Просто использовать его не имеет никакого смысла! Следовательно, спин-блокировки в системах Uni заменяются на preempt_disable во время компиляции ядром!

...