hostprofi.ru
Подобрать хостинг
Термин·буква R

Race condition

краткое определение

Race condition (состояние гонки) — ошибка в многопоточном коде, когда результат программы непредсказуемо зависит от порядка выполнения потоков, одновременно обращающихся к общим данным без надлежащей синхронизации.

Race condition возникает, когда два или более потока или процесса одновременно обращаются к общим данным, и хотя бы один из них выполняет запись. Результат зависит от точного порядка выполнения, который определяет планировщик ОС — непредсказуемо. Это один из наиболее сложно воспроизводимых классов ошибок: баг проявляется редко, часто пропадает при добавлении отладочного кода (Heisenbug).

Как работает

Классический пример: два потока одновременно читают значение счётчика (counter = 5), оба увеличивают его на 1, оба записывают. Ожидаемый результат — 7. Реальный — 6, потому что оба прочитали 5, не увидев записи друг друга. Операция counter++ в большинстве языков не атомарна: это три операции ЦП (read, increment, write).

Методы синхронизации для предотвращения race condition:

  • Mutex (мьютекс) — взаимное исключение. Только один поток может удерживать мьютекс одновременно. pthread_mutex_lock() в C, synchronized в Java, threading.Lock() в Python.
  • Semaphore (семафор) — обобщённый мьютекс с счётчиком: позволяет N потокам одновременно. Используется для ограничения числа параллельных операций.
  • Atomic операции — аппаратно атомарные операции чтения-изменения-записи: std::atomic в C++, java.util.concurrent.atomic, sync/atomic в Go. Быстрее мьютексов для простых счётчиков.
  • Immutability (неизменяемость) — если данные неизменны после создания, race condition невозможна. Функциональный подход.
  • Lock-free структуры данных — Compare-And-Swap (CAS) операции без блокировок: очереди, стеки без мьютексов.

Инструменты обнаружения race condition: ThreadSanitizer (TSan) для C/C++/Go — компилятор вставляет инструментацию, которая обнаруживает гонки в runtime; Helgrind (Valgrind); Java Concurrency Stress (jcstress).

История

Термин "race condition" появился в теории электронных схем в 1950-х годах (Edsger Dijkstra упоминает концепцию в работах 1965 года). В 1965 году Dijkstra сформулировал проблему взаимного исключения и предложил семафоры как решение. Алгоритм Петерсона (1981) решает задачу взаимного исключения для двух потоков без аппаратной поддержки. С ростом многоядерных процессоров (2005+) race conditions стали системной проблемой серверной разработки.

На что обращать внимание

В веб-разработке race condition встречается в: параллельных запросах к БД без транзакций (двойное списание, дублирование заказа), кэшировании при Cache stampede (многие запросы одновременно промахиваются и идут к БД), очередях задач без атомарных операций захвата. На уровне хостинга: PHP-FPM с несколькими worker-процессами, использующими общий файл-семафор или запись в файл без блокировки.

История изучения Race Condition

Термин «race condition» введён в технической литературе в 1954 году в контексте аналоговых электронных схем. В программировании формализован Дейкстрой при разработке алгоритмов взаимного исключения (mutex) в 1965 году. Семафоры — механизм синхронизации, описанный Дейкстрой в 1968 году. Helgrind (Valgrind) — инструмент обнаружения гонок в C/C++ появился в 2002 году. Go Race Detector встроен в компилятор Go с 2012 года.

Классические примеры Race Condition

# PHP пример без защиты — race condition
$count = get_counter_from_db();  // читаем = 100
$count++;                        // увеличиваем
save_counter_to_db($count);      // сохраняем = 101
# Два одновременных запроса оба прочтут 100, оба запишут 101

# Правильно — атомарная операция в Redis
$redis->incr('counter');  // атомарно

Как предотвратить Race Condition на хостинге

МеханизмПрименение
Mutex/Lockодин процесс в PHP
Redis SETNX / Redlockраспределённая блокировка
Database transactionsатомарные операции в MySQL/PostgreSQL
SELECT FOR UPDATEблокировка строки в БД
Atomic operationsRedis INCR, PostgreSQL SEQUENCE

Типичные ошибки

  • Чтение-изменение-запись без транзакций в многопроцессной среде PHP-FPM.
  • Использование файловых блокировок (flock()) для распределённых VPS: работает только на одном сервере.
  • Deadlock при неправильном порядке получения блокировок: A ждёт B, B ждёт A.
  • Игнорирование race condition в тестах: ошибки проявляются только при высокой конкурентности.

Другие термины