Стан гонитви
Стан гонитви (англ. race condition, race hazard) — стан електроніки, програмного забезпечення або іншої системи, де поведінка системи залежить від того, в якій послідовності, або ж з якою тривалістю виконуються окремі інструкції коду. Це стає помилкою, коли одна або кілька можливих дій є небажаними.
Стан гонитви є специфічною помилкою проектування системи, трапляється в багатопотокових і розподілених програмах. Проявляється неконтрольовано у випадкові моменти часу і досить часто симптоми стану гонитви зникають, якщо спробувати її злокалізувати.
Свою назву отримала від схожої помилки проектування електронних схем та поняття «перегонів сигналів».
Стани гонитви проявляються в застосунках, де окремі процеси або потоки виконання залежать від одного спільного стану. Дії над спільним станом є критичними секціями, що мають бути несумісними. Невдача в цьому призводить до можливості пошкодження спільного стану.
Стани гонитви горезвісно складні для відтворення і зневадження, бо кінцевий результат не є детерміністичним і дуже залежить від відносного перебігу виконування процесів, що взаємодіють. Проблеми, що зустрічаються в готових системах можуть зникнути при запуску в режимі зневадження, коли відбувається додаткове журналювання і перевіряння або коли приєднується зневаджувач. Часто такий стан називають гайзенбагом. Дуже радять уникати станів гонитви через уважне розробляння програми, а не через виправляння проблеми потім.
Як простий приклад розглянемо два потоки, що хочуть збільшити на одиницю глобальну цілочисельну змінну. В ідеалі, потрібна така послідовність операцій:
Потік 1 | Потік 2 | Ціле | |
---|---|---|---|
0 | |||
прочитати | ← | 0 | |
збільшити | 0 | ||
записати | → | 1 | |
прочитати | ← | 1 | |
збільшити | 1 | ||
записати | → | 2 |
так ми отримаємо 2, як і очікували. Однак, якщо два потоки запустяться одночасно без блокування або синхронізації:
Потік 1 | Потік 2 | Ціле | |
---|---|---|---|
0 | |||
прочитати | ← | 0 | |
прочитати | ← | 0 | |
збільшити | 0 | ||
збільшити | 0 | ||
записати | → | 1 | |
записати | → | 1 |
Кінцеве значення 1, а не 2. Це станеться через те, що операції збільшення виконуються сумісно. несумісні дії — це такі, які не можна перервати під час доступу до деяких ресурсів, таких як пам'ять. У першому випадку, Потік 1 не переривали під час доступу до змінної, отже його операція була несумісною.
Термін зазвичай стосується ситуації, коли операція з пам’яттю в одному потоці потенційно може спробувати отримати доступ до місця пам’яті, в яку записуються дані в іншому потоці в контексті, де це небезпечно. Це означає, що data race відрізняється від стану гонитви, оскільки можливий недетермінізм через синхронізацію навіть у програмі без data race, наприклад, у програмі, у якій усі звернення до пам’яті використовують лише атомарні операції.
Це може бути небезпечно на багатьох платформах тому, що якщо два потоки записують у область пам’яті одночасно, можливо, що область пам’яті зрештою буде містити довільну комбінацію бітів, що представляють значення, які кожен потік намагався записати. Це може призвести до пошкодження пам’яті, якщо отримане значення жоден з потоків не намагався записати. Подібним чином, якщо один потік читає з певного місця у той час, як інший потік записує в нього, для читання можливо повернеться значення, яке є довільною комбінацією бітів, що представляють значення, яке місце пам’яті зберігало до запису, і бітів, що представляють значення, що записується.
На багатьох платформах передбачені спеціальні операції (атомарні операції, або операції синхронізації) з пам'яттю для одночасного доступу; у таких випадках зазвичай одночасний доступ із використанням цих спеціальних операцій є безпечним, на відміну від інших операцій (операцій з даними).
Стандарт С++11 у параграфі 1.10 визначає стан гонитви так:
- Обчислення двох виразів конфліктує якщо один з виразів змінює ділянку пам'яті, а другий читає або також змінює ту саму ділянку пам'яті.
- Перебіг програми має стан гонитви, якщо він містить в різних потоках дві дії, що конфліктують, щонайменше одна з яких не атомарна і жодна не відбувається перед іншою.
![]() |
Це незавершена стаття про програмування. Ви можете допомогти проєкту, виправивши або дописавши її. |