Чи варто подбати про умови перегонів, які майже напевно не мають шансів виникнути?


52

Розглянемо щось на зразок програми GUI, де головний потік оновлює інтерфейс користувача миттєво, а інший потік запитує дані по мережі або щось, на що гарантовано потрібно 5-10 секунд, щоб закінчити завдання.

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

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


21
Коли люди заявляють про такі шанси, чому ніхто не запитує про освіту людини, яка вказує це число? Вам потрібна формальна освіта зі статистики, перш ніж ви зможете створити резервну копію такої кількості.
Пітер Б

27
Як фізик, p <1E-140 означає p = 0. У цьому Всесвіті це не відбудеться. 0,000000000000000000000000000000000000000000000000000000001% набагато більше.
MSalters

15
Переконайтесь, що такий стан гонки не може призвести до того, що хтось охоче збоїть ваш додаток. Це може бути причиною проблеми із безпекою.
тостів_флякс

27
Кожен мільйон шансів трапиться дев'ять разів із десяти.
Kaz Dragon

27
"майже точно не має шансів виникнути?" значить, це відбувається у виробництві о 3 ранку і, швидше за все, коштує дуже дорого.

Відповіді:


137

Якщо це дійсно подія 1 на 10 ^ 55, кодувати його не потрібно. Це означає, що якби ви робили операцію 1 мільйон разів на секунду, ви отримували б одну помилку кожні 3 * 10 ^ 41 рік, що, приблизно, в 10 ^ 31 раз перевищує вік Всесвіту. Якщо ваша програма має помилку лише один раз на кожні трильйон трильйонів мільярдів віків Всесвіту, це, мабуть, досить надійно.

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


66
Мені нагадано коментар, який я читав роки тому, але зараз не можу знайти "Шанс 1 на мільйон, як правило, наступного вівторка". +1 за те, що він сказав, що "ніде не так малоймовірно".
Беван

2
+1 за ставку. Найкращий спосіб впоратися з умовами раси - це позбутися їх.
Blrfl

10
@Bevan "Шанс 1 на мільйон, як правило, наступного вівторка" ... якщо ви не граєте в лотерею :)
dasblinkenlight

22
@dasblinkenlight Але шанси на те, що хтось виграє у більшості лотерей, наближаються до 100%. Прогнозувати кого , зараз це виклик.
Беван

3
@ Bevan: Цей коментар був саме тим, що проходило мені в голові, коли я читав питання - ось посилання: blogs.msdn.com/b/larryosterman/archive/2004/03/30/104165.aspx
Doc Brown

69

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

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

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


20
+1: Розмежування "Невдача, яка виглядає як невдача", і "Невдача, яка виглядає як успіх". Неправильна інформація набагато серйозніша, залежно від домену.
deworde

2
+1 це має велику різницю, якими можуть бути результати стану гонки.
Грант

+1 Наслідок стану перегонів повинен бути головним вирішальним фактором, якщо до нього слід звернути увагу. Стан гонки, який може спричинити аварію літака, сильно відрізняється від умови, що може змусити користувача повторно відкрити додаток.
ткнути

1
+1: Я б сказав, що наслідки - це, мабуть, те, що ви повинні аналізувати, а не ймовірність виникнення. Якщо наслідки не мають значення, вам, можливо, не доведеться впоратися з умовами гонки ВСІМ, якщо це дуже часто.
Лев

1
Але не вважайте, що виправлення умови перегонів автоматично означає, що вам доведеться написати більше коду. Це може так само означати, що вилучіть великий шматок баггі-коду та замініть його на менший фрагмент правильного коду.
JesperE

45

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

Що стосується ймовірностей, ви були б здивовані. У минулому році у мене був звіт про помилку щодо стану гонки, що я не міг відтворити тисячі автоматизованих спроб, але одна система одного клієнта бачила це постійно. Ділова цінність витрачати 5 хвилин, щоб виправити його зараз, і, можливо, усунення неполадок "неможливої" помилки при встановленні клієнта, робить вибір не потрібним.


1
Це теж! Не допускайте, щоб інші програмісти замислювалися над можливими проблемами під час читання вашого коду, роблячи необхідне (навіть якщо це "навряд чи" вийде з ладу).
Кейсі Кубалл

Ваша точка зору добре (виправлення, зроблені зараз, швидші та дешевші, ніж ті, що були зроблені пізніше), за винятком того, що це ніколи не буде "5 хвилин, щоб виправити це зараз".
іконоборство

2
+1 для вказівки, що ймовірність стану гонки, ймовірно, залежить від багатьох факторів, тому навіть якщо це виглядає малоймовірним у вашій конфігурації, це може траплятися частіше в системі клієнтів / на іншій ОС / у наступному випуску тощо.
sleske

27

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


6
+1 для зміни алгоритмів. Зараз, коли вам відомо про стан гонки, ймовірність низька. Через рік, забувши про стан гонки, ви можете внести зміни до свого коду, що суттєво змінить терміни та ймовірність помилки.
Філ

13

а деякі інші потоки - це опитування даних по мережі або щось, на що гарантовано знадобиться 5-10 секунд, щоб закінчити завдання.

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

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

Завжди фіксуйте умови перегонів, якщо ви їх розпізнаєте.


8

Простий проти правильний.

У багатьох випадках простота козирує правильність. Це питання вартості.

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

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


1
+1 для входу в систему та невдалого запуску, якщо виправити це прямо, занадто складно.
Мартін Ба

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

@reirab Я не згоден. Якщо ви вважаєте нечасті події, то помилка входу в систему є економічно вигідною. Приклад: якщо ваш додаток телефону має коефіцієнт відмов 1/100 (аварія), якщо користувач перемикає мережу в точний місячний перехід (1/31 23:59:00 -> 2/1 00:00:00), ви Напевно, я ніколи про це не почую. Але тоді шанс 1/10 ^ 9 збоїв при підключенні до сервера неприйнятний. Це залежить.
ptyx

7

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

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

Навіть якщо умова перегонів має щось на зразок 10 ^ (- 80) шансів статися випадковим чином , цілком може статися так, що рішучий нападник має гідні шанси створити такі умови навмисно та штучно.


6

Therac-25!

Розробники проекту Therac-25 були досить впевнені в термінах між інтерфейсом користувача та проблемою, пов’язаною з інтерфейсом, у терапевтичній машині XRAY.

Вони не повинні були бути.

Дізнатися більше про цю знамениту катастрофу програмного забезпечення на смерть та смерть можна за посиланням:

http://www.youtube.com/watch?v=izGSOsAGIVQ

або

http://en.wikipedia.org/wiki/Therac-25

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

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

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

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

Здається, що обчислення ймовірності відбувається від когось іншого. Чи знають вони ваш код і чи знаєте ви їх достатньо, щоб довіритися, що помилки не було? Якщо я обчислив 99,99997% надійності для чогось, я також міг би подумати про свої статистичні заняття в коледжі і пам’ятати, що я не завжди отримував 100% і відступав зовсім небагато відсотків на своїх власних особистих оцінках надійності.


1
+1 для згадки про Therac-25. Тут багато важливих уроків.
Стюарт Маркс

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

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

4

було б абсолютно непотрібним або навіть контрпродуктивним додати більше рядків коду, щоб перешкодити читанню?

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

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


3

Це залежатиме від контексту. Якщо це випадкова гра на iPhone, напевно, ні. Мабуть, система управління польотом для наступного пілотованого космічного апарату. Все залежить від того, які наслідки будуть, якщо «поганий» результат відбудеться, виміряний із розрахунковою вартістю його виправлення.

На такі типи питань рідко зустрічається відповідь "один розмір, який відповідає всім", оскільки вони не є питаннями програмування, а натомість питань економіки.


3
"Система управління польотом для наступного пілотованого космічного апарату" ВИЗНАЧЕНО .
deworde

певно ... напевно ... це залежало б від того, хто був у ракеті :-)
GrandmasterB

3

Так, очікуйте несподіваного. Я провів години (у інших народних кодах ^^), відслідковуючи умови, які ніколи не повинні відбуватися.

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

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


3

Просто виправте це.

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

Деякий клієнт десь вирішить одного дня запустити щось, що зависає весь час процесора для «швидкої» нитки, залишаючи повільний потік, і вам буде шкода :)


1

Якщо ви визнали малоймовірною умову гонки, принаймні задокументуйте це у коді!

EDIT: Я повинен додати, що я би це виправив, якщо це було можливо, але під час написання вищезазначеної жодна інша відповідь прямо не сказала принаймні документувати проблему в коді.


1
Так, і, принаймні, спробуйте виявити його та записати його, якщо це станеться. ІМХО цілком чудово не уникати кожної помилки. Але принаймні дайте комусь знати, що це сталося, і що ваше припущення, що це не було, було помилковим.
Стів Беннетт

0

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


0

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

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


0

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

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

Receive event from device:
{
    Record event details.
    Enqueue event in the queuing thread.
    Acknowledge the event.
}

Queueing thread receives an event:
{
    Retrieve event details.
    Process event.
    Send next command to device.
}

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

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

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