Ответ
Такая ситуация называется состоянием гонки (Data Race) и является неопределённым поведением (Undefined Behavior) согласно стандарту C++. Программа может вести себя непредсказуемо.
Возможные последствия:
- Повреждение данных: Результат операции может быть некорректным.
- Критические сбои: Повреждение структур данных кучи (heap corruption), ведущее к падению программы.
- Несогласованное состояние: Разные потоки увидят разные значения переменной.
- Проблемы с оптимизацией: Компилятор вправе переупорядочивать операции с памятью в отсутствие синхронизации, что может привести к контринтуитивным результатам.
Пример гонки данных:
#include <iostream>
#include <thread>
#include <vector>
int shared_counter = 0; // Разделяемая переменная
void increment() {
for (int i = 0; i < 100000; ++i) {
++shared_counter; // НЕАТОМАРНАЯ операция: чтение-изменение-запись
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
// Результат почти гарантированно будет меньше 200000
std::cout << "Final counter value: " << shared_counter << std::endl;
return 0;
}
Способы обеспечить безопасный доступ:
- Мьютексы (
std::mutex,std::shared_mutex): Гарантируют эксклюзивный доступ. - Атомарные операции (
std::atomic<T>): Для простых типов данных. В примере выше нужно было использоватьstd::atomic<int> shared_counter{0};. - Lock-free структуры данных: Для высокопроизводительных сценариев.
- Локальная память потока (thread_local): Если данные не нужно разделять.
Правило: Любой доступ (чтение или запись) к неатомарному объекту, который может быть изменён из другого потока, должен быть защищён механизмом синхронизации.