Що заважає умові перегонів на замку?


24

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

Що ж відбувається тоді? Що робиться для запобігання цього? Це неможливо чи просто очевидне? Або це реальна умова гонки, яка чекає, що станеться?


Це питання було задано раніше на SO: stackoverflow.com/questions/980521/…
Doc Brown

і пов'язане питання тут на P.SE: programmers.stackexchange.com/questions/228827/…
храповик виродка

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

Ви пропустили важливий момент в тому , як замки роботи. Вони побудовані так, що не можна проводити перегони на замку, інакше вони абсолютно марні.
Зейн

Відповіді:


36

Це неможливо чи просто очевидне?

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


3
Слава ... Боже ... це обробляється апаратно ... (або принаймні рівень нижчий, ніж ми торкаємось)
corsiKa

2
@gdhoward Я не можу повірити ... ця відповідь зайняла мені менше 5 хвилин, і це третя найвища кількість голосів з моїх чотирьох сотень відповідей (переважно ТАК). А також, мабуть, найкоротший.
maaartinus

1
@maaartinus - Короткий і солодкий іноді це робить.
Бобсон

17

Вивчіть поняття атомних операцій «Випробування та встановлення».

По суті, операцію не можна розділити - дві речі не можуть робити це точно в один і той же час. Він перевірить значення, встановить його, якщо воно ясно, і поверне значення таким, яким воно було під час тестування. У режимі блокування результат після тестування і встановлення завжди буде "замок == ІСТИНА", єдиною різницею було те, встановлено він чи ні на початку.

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


2
В основному, якщо обладнання не є внутрішньо послідовним на певному рівні, воно матиме механізм, який дозволяє йому розривати зв'язки, які можуть виникнути в іншому випадку.
Білл Мішель

@BillMichell, я мав би про це подумати. Власне, я і зробив; Я просто не знав, чи було моє припущення правильним.
Гевін Говард

2

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

Більшу частину часу використовуються атомні цикли порівняння та встановлення, які виконуються на апаратному рівні

while(!CompareAndSet(&lock, false, true));//busy loop won't continue until THIS thread has set the lock to true
//critical section
CompareAndSet(&lock, true, false);

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


1

Неможливо, щоб дві (або більше) ниток придбали замок одночасно. Наприклад, існує кілька типів методів синхронізації:

Активне очікування - відключення блокування

Псевдокод:

1. while ( xchg(lock, 1) == 1); - entry protocole

XCHG - приклад атомної роботи (існує в архітектурі x86), який спочатку встановлює нове значення для змінної "lock", а потім повертає старе значення. Атомний означає, що його неможливо перервати - у наведеному вище прикладі між встановленням нового значення та поверненням старого. Атомно - детермінований результат незалежно від того.

2. Your code
3. lock = 0; - exit protocol

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

Призупинення нитки - наприклад, підрахунок семафору

Існує дві атомні операції, .Wait()і у .Signal()нас є ціла змінна давайте її назвати int currentValue.

Wait():
if (currentValue > 0) currentValue -= 1;
else suspend current thread;

Signal():
If there exists thread suspended by semaphore wake up one of them
Else currentValue += 1;

Зараз вирішити проблему критичного розділу дуже просто:

Псевдокод:

mySemaphore.Wait();
do some operations - critical section
mySemaphore.Signal();

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.