Как ответить
Race condition (состояние гонки) — это ситуация, когда результат работы многопоточной или многопроцессной программы зависит от того, в каком порядке выполняются потоки или процессы. Если порядок непредсказуем, программа может выдавать разные результаты при одинаковых входных данных, включая ошибки и крахи.
Самый наглядный пример — инкремент разделяемой переменной двумя потоками без синхронизации:
// Разделяемая переменная int counter = 0;
// Поток 1:
int tmp = counter;
tmp = tmp + 1;
counter = tmp;
// Поток 2:
int tmp = counter;
tmp = tmp + 1;
counter = tmp;Ожидаемый результат: после выполнения обоих потоков counter = 2. Но из-за гонки может получиться 1:
- Поток 1 прочитал counter (0), затем переключился на Поток 2.
- Поток 2 прочитал counter (0), увеличил до 1, записал 1.
- Поток 1 продолжил: увеличил свою копию 0 до 1, записал 1.
Это data race — частный случай race condition, когда два потока одновременно обращаются к одной ячейке памяти, и хотя бы один из них пишет.
Race condition бывает и без data race. Пример — операция «проверить-действие» (check-then-act):
if (account.balance >= amount) {
account.withdraw(amount);
}Без синхронизации два потока могут одновременно пройти проверку, и оба снимут деньги, хотя баланс позволяет только один снятие. Гонки данных здесь нет, если баланс читается атомарно, но логическая гонка есть.
Основные способы борьбы:
- Мьютексы (mutex) — защита критической секции.
- Атомарные операции (например,
std::atomic<int>в C++ илиAtomicIntegerв Java) — гарантируют неделимость чтения-модификации-записи. - Блокировки семафоры, условные переменные — для сложной координации.
- В базах данных — транзакции с изоляцией (SERIALIZABLE) или оптимистичные блокировки.
Главная сложность — race condition недетерминирована: она может проявляться раз на миллион запусков, что делает её отладку крайне трудной. Поэтому на production-системах синхронизацию закладывают архитектурно, а не «чинят» после инцидента.