Re[3]: Порядок создания объектов
От: B0FEE664  
Дата: 31.05.23 15:34
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>А если у нас есть инициализация переменной ?

Простая инициализация сама по себе мало что меняет, а вот использование переменной — да.

_NN>
_NN>int i{ 10 };
_NN>std::thread t( [&i] { printf("%d", i); } );
_NN>

_NN>Как я понимаю даже если компилятор понимает, что порядок менять нельзя, присваивание значения 10 может не отразится при чтении из другого потока. Верно ?
Нет. В этом случае i гарантированно будет инициализировано до создания ссылки на i.

Так как i — ссылка, то напечатано будет фактическое значение i на момент копирования значения i при передаче в функцию printf. Т.е. вот такой код:
int i{ 10 };
std::thread t( [&i] { printf("%d", i); } );
i += 1;

Может напечатать 11, (а может любое другое значение, на архитектуре, где i не атомарная переменная).

_NN>То есть даже в таком коде нам нужен барьер памяти, чтобы поток прочитал именно то, что нужно.

Если переменная i не меняется, то никаких проблем.

Ну и вот такой код однозначно выведет 10:

int i{ 10 };
std::thread t( [i] { printf("%d", i); } );
i += 1;


_NN>В таком случае atomic<int> также не спасает ведь инициализация не автомарная:

_NN>
_NN>std::atomic<int> i{ 10 }; // 2) Initializes the underlying object with desired. The initialization is not atomic.
_NN>std::thread t( [&i] { printf("%d", i); } );
_NN>

Этот код не скомпилируется, но если написать
std::atomic<int> i{ 10 }; // 2) Initializes the underlying object with desired. The initialization is not atomic.
std::thread t( [&i] { printf("%d", i.load()); } );

то будет выведено значение 10 или другое корректное значение, если i изменился:
std::atomic<int> i{ 10 }; 
std::thread t( [&i] { printf("%d", i.load()); } );
i += 1;

— будет выведено либо 10, либо 11.
Это даже на прямую не связано с многопоточностью, а с тем что если переменная участвует в выражении, то значение переменной берётся из вычисления предыдущего выражения. В данном случае предыдущее выражение — это инициализация. Поэтому инициализация должна быть завершена до того, как переменную будут использовать через ссылку.

Если я правильно понимаю, то проблема с not thread-safe инициализацией может возникнуть только при использовании не static глобальной переменной в нескольких потоках. Или если саму переменную пытаются использовать прямо в выражении инициализации типа такого:
std::atomic<int> i{[&i]{ std::thread t( [&i] { printf("%d", i.load()); } ); return 2;}()};

— это, вообще говоря, разновидность UB вида int i = 1 + i;
И каждый день — без права на ошибку...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.