Коли слід використовувати спінлок замість мютексу?


294

Я думаю, що обидва роблять одну і ту ж роботу, як ви вирішите, яку саме використовувати для синхронізації?


1
можливий дублікат Spinlock Versus Semaphore!
Пол Р.

11
Mutex і Semaphore - це не одне і те ж, тому я не думаю, що це дублікат. У відповіді на посилання на цю статтю зазначено правильно. Більш детально див. Barrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore
nanoquack

Відповіді:


729

Теорія

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

Проблема

Проблема з мютексами полягає в тому, що укладання ниток уві сні і прокидання їх знову - це досить дорогі операції, їм знадобиться досить багато інструкцій щодо процесора, і, таким чином, також знадобиться певний час. Якщо зараз мютекс був заблокований лише дуже короткий проміжок часу, час, витрачений на укладення нитки для сну та її пробудження знову, може перевищити час, коли нитка насправді спала, і навіть може перевищити час, коли нитка були витрачені на постійне опитування на вертушку. З іншого боку, опитування спінлок буде постійно витрачати час процесора, і якщо блокування буде триматися довше часу, це витратить набагато більше часу на процесор, і це було б набагато краще, якби нитка спала замість цього.

Рішення

Використовувати спінкі в одноядерній / однопроцесорній системі, як правило, не має сенсу, оскільки поки опитування блокування блокує єдине доступне ядро ​​CPU, жоден інший потік не може працювати і оскільки жоден інший потік не може працювати, блокування не буде бути розблокованим також. IOW, спінклок витрачає лише процесорний час на ці системи без жодної реальної користі. Якщо нитка замість цього була переведена у режим сну, інша нитка могла запуститись одразу, можливо, розблокувавши замок, а потім дозволивши першій нитці продовжити обробку, як тільки вона знову прокинеться.

У багатоядерних / багатопроцесорних системах, з великою кількістю блокувань, які тримаються лише дуже короткий час, витрачений час на постійне введення ниток у режим сну та їх повторне пробудження може помітно знизити продуктивність виконання. Використовуючи натомість спінові блоки, потоки отримують шанс скористатися своїм повним квантом виконання (завжди блокується лише дуже короткий проміжок часу, але потім негайно продовжують свою роботу), що призводить до набагато більшої пропускної здатності.

Практика

Оскільки дуже часто програмісти не можуть заздалегідь знати, чи будуть кращі мьютекси або спіноли (наприклад, тому, що кількість ядер CPU цільової архітектури невідоме), а також операційні системи не можуть знати, чи певний код був оптимізований для одноядерних або багатоядерні середовища, більшість систем не чітко розмежовують між мютексами та спілоксами. Насправді, більшість сучасних операційних систем мають гібридні мютекси та гібридні спінки. Що це насправді означає?

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

Гібридний спінлок веде себе як звичайний спінклок спочатку, але, щоб не витрачати занадто багато часу на процесор, він може мати стратегію відступу. Зазвичай він не вкладає потік у режим сну (оскільки ви не хочете, щоб це сталося під час використання спінклока), але він може вирішити припинити потік (негайно або через певний проміжок часу) і дозволити запустити інший потік , тим самим збільшуються шанси на розблокування (чистий перемикач ниток зазвичай дешевший, ніж той, що передбачає укладання нитки спати і прокидання її знову пізніше, хоча і далеко не далеко).

Підсумок

Якщо ви сумніваєтесь, використовуйте мутекси, вони, як правило, є кращим вибором, і більшість сучасних систем дозволять їм закручуватися протягом дуже короткого часу, якщо це здасться корисним. Використання спинлоків іноді може покращити продуктивність, але лише за певних умов, і факт, що ви сумніваєтесь, швидше підказує мені, що ви не працюєте над будь-яким проектом, де спінінг може бути корисним. Ви можете розглянути можливість використання власного "об'єкта блокування", який може використовувати спінінг або мютекс внутрішньо (наприклад, така поведінка може бути налаштована під час створення такого об'єкта), спочатку використовуйте мутекси скрізь, і якщо ви думаєте, що використання спінлок може десь реально допоможіть, спробуйте і порівняйте результати (наприклад, за допомогою профілера), але обов'язково протестуйте обидва випадки,

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

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

Проблема з'являється наступним чином: Ваш код отримує спінлок в потоці низького пріо класу, і, поки він знаходиться в середині цього блокування, квантовий час перевищив і потік перестає працювати. Єдиний спосіб, як цей спінлок може бути випущений знову, це якщо нитка низького пріорі класу отримує час процесора знову, але це не гарантовано відбудеться. У вас може бути кілька ниток високого пріоритетного класу, які постійно хочуть запускатися, і планувальник завдань завжди надаватиме їм пріоритет. Один з них може пробігати через спінлок і намагатися отримати його, що, звичайно, неможливо, і система зробить це врожайним. Проблема полягає в тому, що нитка, що вийшла, одразу доступна для повторного запуску! Маючи вищий пріоритет, ніж нитка, що тримає замок, у потоку, що тримає замок, немає шансів отримати час виконання процесора.

Чому ця проблема не виникає з мютексами? Коли нитка з високим пріором не зможе отримати мютекс, вона не вийде, вона може трохи закрутитися, але з часом буде відправлена ​​до сну. Спляча нитка не доступна для запуску, поки її не прокине подія, наприклад, подію на зразок розблокованої мютекс, яку вона чекала. Apple усвідомлює цю проблему, і внаслідок цього застаріла OSSpinLock. Новий замок називається os_unfair_lock. Цей блокування дозволяє уникнути згаданої вище ситуації, оскільки він знає про різні класи пріоритетності потоку. Якщо ви впевнені, що використання спинлоків є хорошою ідеєю у вашому проекті iOS, використовуйте його. Тримайся подалі відOSSpinLock! І ні в якому разі не реалізовуйте власні спинлок в iOS! Якщо сумніваєтесь, використовуйте мютекс! macOS не зачіпає цю проблему, оскільки у нього є інший планувальник потоків, який не дозволить жодному потоку (навіть нитки з низьким пріоритетом) «просохнути» на час процесора, все одно там може виникнути така ж ситуація, що призведе до дуже поганого продуктивність, таким чином OSSpinLock, застаріла і на macOS.


3
чудове пояснення ... У мене є сумніви щодо спінка, я можу використовувати спінлок в ISR? якщо ні, чому б ні
харіс

4
@Mecki Якщо я не помиляюся, я вважаю, що ви запропонували у своїй відповіді, що скорочення часу відбувається лише в однопроцесорних системах. Це неправильно! Ви можете використовувати блокування віджиму в однопроцесорній системі, і воно буде крутитися, поки не закінчиться квантовий час. Тоді ще одна нитка такого ж пріоритету може перейняти (як і те, що ви описали для багатопроцесорних систем).
fumoboy007

7
@ fumoboy007 ", і він буде крутитися доти, доки не закінчиться його квантовий час" // Що означає, що ви витрачаєте час процесора / заряд батареї на абсолютно нічого, не маючи жодної вигоди, що є абсолютно монічним. І ні, я ніде не говорив, що часове відсікання відбувається лише в одноядерних системах, я говорив, що в одноядерних системах існує ТІЛЬКИ час розрізання, в той час як існує РЕАЛЬНИЙ паралелізм у багатоядерних системах (а також відсікання часу, але не має значення для того, що я написав у своїй відповідь); Також ви повністю пропустили думку про те, що таке гібридний спінлок, і чому він добре працює в одиночних і багатоядерних системах.
Mecki

11
@ fumoboy007 Нитка А тримає замок і переривається. Нитка B працює і хоче замок, але не може його дістати, тому він крутиться. У багатоядерній системі Нитка А може продовжувати працювати на іншому ядрі, поки Нитка B все ще крутиться, звільнить замок, а Нитка B може продовжуватися протягом поточного кванту часу. У єдиній ядерній системі є лише одна серцевина A Thread A, яку можна запустити, щоб звільнити замок, і це ядро ​​зайняте спінінг B Thread. Таким чином, немає ніякого способу, щоб спінлок можна було випустити до того, як нитка B перевищить свій квантовий час, і, отже, все спінінг - це лише марна трата часу.
Mecki

2
Якщо ви хочете дізнатися більше про спін - блокувань і м'ютекси , реалізованих в ядрі Linux, я настійно рекомендую прочитати главу 5 великих драйверів в Linux, Третє видання (LDD3) (м'ютекси сторінка 109, спін - блокування: стор 116).
patryk.beza

7

Продовжуючи пропозицію Мекі, ця стаття pthread mutex vs pthread spinlock в блозі Олександра Сандлера, Alex в Linux показує, як spinlock& mutexesможна реалізувати тестування поведінки за допомогою #ifdef.

Однак обов'язково прийміть остаточний дзвінок на основі ваших спостережень, розуміючи, що наведений приклад є окремим випадком, ваша вимога проекту, оточення можуть бути зовсім іншими.


6

Зауважте також, що в певних середовищах та умовах (наприклад, запуск у вікнах на рівні відправки> = DISPATCH LEVEL), ви не можете використовувати mutex, а швидше спінлок. На unix - те саме.

Ось рівнозначне запитання на сайті конкурента stackexchange unix: /unix/5107/why-are-spin-locks-good-choices-in-linux-kernel-design-instead-of-something- більше

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


6

Відповідь Мецкі досить добре це приховує. Однак на одному процесорі використання відключення може мати сенс, коли завдання чекає на блокування, яке повинно бути надано службою переривання служби. Переривання передасть управління ISR, який би готовий ресурс для використання завданням очікування. Закінчилося б звільненням блокування перед тим, як повернути контроль до перерваного завдання. Завдання спінінгу знайде доступний прядок і продовжить.


2
Я не впевнений, що повністю згоден з цією відповіддю. Один єдиний процесор, якщо завдання містить блокування ресурсу, ISR не може безпечно продовжувати і не може чекати, коли завдання розблокується ресурсом (оскільки завдання, що містить ресурс, було перервано). У таких випадках завдання повинно просто відключити переривання, щоб забезпечити виключення між собою та ISR. Звичайно, це доведеться робити за дуже короткі проміжки часу.
користувач1202136

3

Механізми синхронізації Spinlock та Mutex сьогодні дуже часто зустрічаються.

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

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

Як правило, найінтуїтивніша ідея стосується синхронізації за допомогою змінної для захисту критичного розділу. Концепція Mutex схожа, але вони все ще відрізняються. Зосередьтеся на: використанні процесора. Spinlock витрачає час процесора на очікування виконання дії, і тому ми можемо підсумувати різницю між двома:

У однорідних багатоядерних середовищах, якщо часу, витраченого на критичну секцію, мало, ніж використання Spinlock, оскільки ми можемо скоротити час переключення контексту. (Одноядерне порівняння не важливо, оскільки деякі системи реалізують Spinlock посеред комутатора)

У Windows за допомогою Spinlock буде оновлено потік до DISPATCH_LEVEL, що в деяких випадках може бути недозволеним, тому цього разу нам довелося використовувати Mutex (APC_LEVEL).


-6

Використовувати спінкі в одноядерній / однопроцесорній системі, як правило, не має сенсу, оскільки, поки опитування спінлок блокує єдине доступне ядро ​​CPU, жоден інший потік не може працювати і оскільки жоден інший потік не може працювати, блокування не буде бути розблокованим також. IOW, спінклок витрачає лише процесорний час на ці системи без жодної реальної користі

Це неправильно. При використанні спінлоків у універсальних процесорних системах не витрачається витрата циклів процесора, тому що, як тільки процес замикається, відключення вимкнення вимикається, тож більше нікого не може бути! Просто використовувати його не має сенсу! Отже, спінблоки в системах Uni замінюються на preempt_disable на час компіляції ядром!


Цитоване все ще цілком вірно. Якщо складений результат вихідного коду не містить спинлок, то цитування не має значення. Якщо припустити, що ви сказали, що правда стосується ядра, яке замінює вимкнення блоків під час компіляції, як обробляються спінблоки при попередньому компілюванні на іншій машині, яка може бути, а може і не бути однопроцесорною, якщо ми чітко не говоримо про спінкі тільки в самому ядрі?
Гідранікс

"Після того, як процес приймає блокування віджиму, вимкнення вимкнено". Виможення не вимкнено, коли процес обертається. Якби це було так, один процес міг би зняти всю машину, просто ввівши спінлок і ніколи не виходячи. Зауважте, що якщо ваш потік працює в просторі ядра (замість простору користувача), вимкнення блокування відключення дійсно вимикає попередження, але я не думаю, що саме тут обговорюється.
Костянтин Вайц

На час компіляції по ядру ?
Шиен

@konstantin Блокування віджиму FYI можна робити лише в просторі ядра. І коли знімається фіксація відключення, на локальному процесорі вимикається виплата.
Neelansh Mittal

@hydranix Ви не отримали? Очевидно, ви не можете скласти модуль проти ядра, в якому увімкнено CONFIG_SMP, та запустити той самий модуль на ядрі, у якого CONFIG_SMP вимкнено.
Neelansh Mittal
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.