Я думаю, що обидва роблять одну і ту ж роботу, як ви вирішите, яку саме використовувати для синхронізації?
Я думаю, що обидва роблять одну і ту ж роботу, як ви вирішите, яку саме використовувати для синхронізації?
Відповіді:
Теорія
Теоретично, коли нитка намагається зафіксувати мютекс, і це не вдається, оскільки мютекс вже заблокований, він перейде у сплячий режим, одразу ж даючи можливість запустити іншу нитку. Він буде продовжувати спати до тих пір, поки не прокинеться, що станеться після того, як мютекс буде розблокований будь-якою ниткою, яка тримала замок раніше. Коли нитка спробує заблокувати спінлок, і це не вдається, він буде постійно повторно намагатися його заблокувати, поки він остаточно не вдається; таким чином, він не дозволить іншому потоку зайняти своє місце (проте, звичайно, операційна система перейде до іншого потоку, звичайно після перевищення кванту виконання поточного процесора поточного потоку, звичайно).
Проблема
Проблема з мютексами полягає в тому, що укладання ниток уві сні і прокидання їх знову - це досить дорогі операції, їм знадобиться досить багато інструкцій щодо процесора, і, таким чином, також знадобиться певний час. Якщо зараз мютекс був заблокований лише дуже короткий проміжок часу, час, витрачений на укладення нитки для сну та її пробудження знову, може перевищити час, коли нитка насправді спала, і навіть може перевищити час, коли нитка були витрачені на постійне опитування на вертушку. З іншого боку, опитування спінлок буде постійно витрачати час процесора, і якщо блокування буде триматися довше часу, це витратить набагато більше часу на процесор, і це було б набагато краще, якби нитка спала замість цього.
Рішення
Використовувати спінкі в одноядерній / однопроцесорній системі, як правило, не має сенсу, оскільки поки опитування блокування блокує єдине доступне ядро CPU, жоден інший потік не може працювати і оскільки жоден інший потік не може працювати, блокування не буде бути розблокованим також. IOW, спінклок витрачає лише процесорний час на ці системи без жодної реальної користі. Якщо нитка замість цього була переведена у режим сну, інша нитка могла запуститись одразу, можливо, розблокувавши замок, а потім дозволивши першій нитці продовжити обробку, як тільки вона знову прокинеться.
У багатоядерних / багатопроцесорних системах, з великою кількістю блокувань, які тримаються лише дуже короткий час, витрачений час на постійне введення ниток у режим сну та їх повторне пробудження може помітно знизити продуктивність виконання. Використовуючи натомість спінові блоки, потоки отримують шанс скористатися своїм повним квантом виконання (завжди блокується лише дуже короткий проміжок часу, але потім негайно продовжують свою роботу), що призводить до набагато більшої пропускної здатності.
Практика
Оскільки дуже часто програмісти не можуть заздалегідь знати, чи будуть кращі мьютекси або спіноли (наприклад, тому, що кількість ядер CPU цільової архітектури невідоме), а також операційні системи не можуть знати, чи певний код був оптимізований для одноядерних або багатоядерні середовища, більшість систем не чітко розмежовують між мютексами та спілоксами. Насправді, більшість сучасних операційних систем мають гібридні мютекси та гібридні спінки. Що це насправді означає?
Гібридний мютекс спершу поводиться як спілок на багатоядерній системі. Якщо нитка не зможе зафіксувати мютекс, він не буде переведений у режим сну негайно, оскільки мютекс може розблокуватися досить скоро, тому натомість мютекс спочатку поводитиметься точно як спінлок. Тільки якщо замок все-таки не був отриманий через певний час (або повторні спроби чи будь-який інший вимірювальний коефіцієнт), нитка дійсно укладається в режим сну. Якщо той самий код працює в системі, що має лише одне ядро, мютекс не закрутиться, хоча, як це бачити вище, це не було б корисно.
Гібридний спінлок веде себе як звичайний спінклок спочатку, але, щоб не витрачати занадто багато часу на процесор, він може мати стратегію відступу. Зазвичай він не вкладає потік у режим сну (оскільки ви не хочете, щоб це сталося під час використання спінклока), але він може вирішити припинити потік (негайно або через певний проміжок часу) і дозволити запустити інший потік , тим самим збільшуються шанси на розблокування (чистий перемикач ниток зазвичай дешевший, ніж той, що передбачає укладання нитки спати і прокидання її знову пізніше, хоча і далеко не далеко).
Підсумок
Якщо ви сумніваєтесь, використовуйте мутекси, вони, як правило, є кращим вибором, і більшість сучасних систем дозволять їм закручуватися протягом дуже короткого часу, якщо це здасться корисним. Використання спинлоків іноді може покращити продуктивність, але лише за певних умов, і факт, що ви сумніваєтесь, швидше підказує мені, що ви не працюєте над будь-яким проектом, де спінінг може бути корисним. Ви можете розглянути можливість використання власного "об'єкта блокування", який може використовувати спінінг або мютекс внутрішньо (наприклад, така поведінка може бути налаштована під час створення такого об'єкта), спочатку використовуйте мутекси скрізь, і якщо ви думаєте, що використання спінлок може десь реально допоможіть, спробуйте і порівняйте результати (наприклад, за допомогою профілера), але обов'язково протестуйте обидва випадки,
Насправді не iOS, але iOS - це платформа, на якій може виникнути більшість розробників із такою проблемою. Якщо у вашій системі є планувальник потоків, це не гарантує, що будь-який потік, яким би низьким не був його пріоритет, з часом отримає шанс запустити, то спіноблоки можуть призвести до постійних тупиків. Планувальник iOS розрізняє різні класи ниток, а потоки нижчого класу запускаються лише в тому випадку, якщо жоден потік у вищому класі також не хоче запускатися. Для цього немає стратегії зворотного відключення, тому якщо у вас постійно доступні потоки високого класу, нитки низького класу ніколи не отримають жодного часу процесора і, таким чином, ніколи не матимуть шансів виконати будь-яку роботу.
Проблема з'являється наступним чином: Ваш код отримує спінлок в потоці низького пріо класу, і, поки він знаходиться в середині цього блокування, квантовий час перевищив і потік перестає працювати. Єдиний спосіб, як цей спінлок може бути випущений знову, це якщо нитка низького пріорі класу отримує час процесора знову, але це не гарантовано відбудеться. У вас може бути кілька ниток високого пріоритетного класу, які постійно хочуть запускатися, і планувальник завдань завжди надаватиме їм пріоритет. Один з них може пробігати через спінлок і намагатися отримати його, що, звичайно, неможливо, і система зробить це врожайним. Проблема полягає в тому, що нитка, що вийшла, одразу доступна для повторного запуску! Маючи вищий пріоритет, ніж нитка, що тримає замок, у потоку, що тримає замок, немає шансів отримати час виконання процесора.
Чому ця проблема не виникає з мютексами? Коли нитка з високим пріором не зможе отримати мютекс, вона не вийде, вона може трохи закрутитися, але з часом буде відправлена до сну. Спляча нитка не доступна для запуску, поки її не прокине подія, наприклад, подію на зразок розблокованої мютекс, яку вона чекала. Apple усвідомлює цю проблему, і внаслідок цього застаріла OSSpinLock
. Новий замок називається os_unfair_lock
. Цей блокування дозволяє уникнути згаданої вище ситуації, оскільки він знає про різні класи пріоритетності потоку. Якщо ви впевнені, що використання спинлоків є хорошою ідеєю у вашому проекті iOS, використовуйте його. Тримайся подалі відOSSpinLock
! І ні в якому разі не реалізовуйте власні спинлок в iOS! Якщо сумніваєтесь, використовуйте мютекс! macOS не зачіпає цю проблему, оскільки у нього є інший планувальник потоків, який не дозволить жодному потоку (навіть нитки з низьким пріоритетом) «просохнути» на час процесора, все одно там може виникнути така ж ситуація, що призведе до дуже поганого продуктивність, таким чином OSSpinLock
, застаріла і на macOS.
Продовжуючи пропозицію Мекі, ця стаття pthread mutex vs pthread spinlock в блозі Олександра Сандлера, Alex в Linux показує, як spinlock
& mutexes
можна реалізувати тестування поведінки за допомогою #ifdef.
Однак обов'язково прийміть остаточний дзвінок на основі ваших спостережень, розуміючи, що наведений приклад є окремим випадком, ваша вимога проекту, оточення можуть бути зовсім іншими.
Зауважте також, що в певних середовищах та умовах (наприклад, запуск у вікнах на рівні відправки> = 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
Відповідь Мецкі досить добре це приховує. Однак на одному процесорі використання відключення може мати сенс, коли завдання чекає на блокування, яке повинно бути надано службою переривання служби. Переривання передасть управління ISR, який би готовий ресурс для використання завданням очікування. Закінчилося б звільненням блокування перед тим, як повернути контроль до перерваного завдання. Завдання спінінгу знайде доступний прядок і продовжить.
Механізми синхронізації Spinlock та Mutex сьогодні дуже часто зустрічаються.
Давайте спочатку подумаємо про Спінлок.
В основному це зайнята дія очікування, а це означає, що нам доведеться чекати, коли визначений замок буде звільнений, перш ніж ми можемо продовжити наступну дію. Концептуально дуже просто, хоча реалізувати це не так. Наприклад: Якщо замок не було випущено, то нитка була замінена та перейшла у стан сну, чи повинні ми з цим мати справу? Як боротися із блокуваннями синхронізації, коли два потоки одночасно вимагають доступу?
Як правило, найінтуїтивніша ідея стосується синхронізації за допомогою змінної для захисту критичного розділу. Концепція Mutex схожа, але вони все ще відрізняються. Зосередьтеся на: використанні процесора. Spinlock витрачає час процесора на очікування виконання дії, і тому ми можемо підсумувати різницю між двома:
У однорідних багатоядерних середовищах, якщо часу, витраченого на критичну секцію, мало, ніж використання Spinlock, оскільки ми можемо скоротити час переключення контексту. (Одноядерне порівняння не важливо, оскільки деякі системи реалізують Spinlock посеред комутатора)
У Windows за допомогою Spinlock буде оновлено потік до DISPATCH_LEVEL, що в деяких випадках може бути недозволеним, тому цього разу нам довелося використовувати Mutex (APC_LEVEL).
Використовувати спінкі в одноядерній / однопроцесорній системі, як правило, не має сенсу, оскільки, поки опитування спінлок блокує єдине доступне ядро CPU, жоден інший потік не може працювати і оскільки жоден інший потік не може працювати, блокування не буде бути розблокованим також. IOW, спінклок витрачає лише процесорний час на ці системи без жодної реальної користі
Це неправильно. При використанні спінлоків у універсальних процесорних системах не витрачається витрата циклів процесора, тому що, як тільки процес замикається, відключення вимкнення вимикається, тож більше нікого не може бути! Просто використовувати його не має сенсу! Отже, спінблоки в системах Uni замінюються на preempt_disable на час компіляції ядром!