#новичкам
Теперь состояние гонки. Это более общее понятие, чем гонка данных. Это ситуация в программе, когда поведение системы зависит от относительного порядка выполнения операций в потоках.
Внимание: состояние гонки есть даже в правильно синхронизированных программах. В однопоточной программе можно четко предсказать порядок обработки элементов. А вот если много потоков будут разгребать одну кучу задач - вы не сможете сказать заранее, какой выхлоп в следующий раз произведет конкретный поток. Потому что это зависит от шедулинга потоков.
Но нам и не важно это предсказание, потому что имеет значение поведение всей программы целиком.
Проблемы возникают, когда такие спорадические эффекты приводят к некорректным результатам. И именно эти ситуации обычно называют состоянием гонки. Мне кажется важным проговорить, что потоки всегда в состоянии гонки за чем-то и в этом отражение недетерминированности многопоточной среды. Но далее буду употреблять "состояние гонки" в негативном контексте.
Проблемы из-за состояния гонки могут происходить даже в программах без гонки данных.
Например:
std::atomic<int> x = 2;
void thread_1() {
x = 3;
}
void thread_2() {
if (x % 2 == 0) {
std::cout << x << std::endl;
}
}
Может так произойти, что поток 1 выполнится в промежутке между условием и выводом
x на консоль. Это очень маловероятная ситуация, однако на консоль может вывестись нечетное число 3 с учетом того, что перед выводом мы проверили на четность. Как минимум удивительный результат, хотя с программе нет гонки данных."Плохое" состояние гонки - это в основном ошибка проектирования в условиях многопоточности. Знаменитая проблема наличия метода size() у многопоточной очереди - состояние гонки:
template <typename T>
class ThreadSafeQueue {
...
size_t size() {
std::lock_guard lg{mtx_};
return queue_.size();
}
private:
std::deque<T> queue_;
...
};
ThreadSafeQueue<int> queue;
...
if (queue.size() > 0) {
auto item = std::move(queue.front());
queue.pop();
// process item
}
Если между успешной и потокобезопасной проверкой, что очередь непустая, придет другой поток и заберет последний элемент из очереди, вы получите ub в попытке увидеть фронтальный элемент.
Основные черты состояния гонки:
🙈 Наличие логической ошибки при проектировании системы
🙈 Зависимость от планирования потоков
🙈 Зависимость от времени выполнения операции. Вчера в чате скинули мем, иллюстрирующий эту зависимость.
Многие путают или не понимают разницы между race condition и data race. Это даже частый вопрос на собеседованиях, на который 50% кандидатов отвечают что-то вообще невнятное. Но теперь вы подготовлены и вооружены правильным словарным аппаратом.
Be independent of other's schedule. Stay cool.
#design #concurrency #interview