У меня есть вопрос, касающийся инициализации атомарных объектов.
Николай Джосаттис пишет:
Обратите внимание, что атомарные объекты всегда стоит инициализировать, потому что конструктор по умолчанию хоть и выполняет инициализацию значения, но может оставить без инициализации состояние блокировки.
То есть, если конкретнее, то мой вопрос заключается в следующем:
class Object
{
public:
Object()
:
flag{ true }// 1
{
flag.store(true);// 2
std::atomic_init(&flag, true);// 3
}
private:
std::atomic<bool> flag;
};
Достаточно ли любого одного из трех способов, чтобы с атомарным объектом все было в порядке?
Есть ли какие-то отличия между этими вариантами?
Достаточно инициализировать flag
первым способом:
class Object
{
public:
Object()
:
flag{ true }// 1
{}
private:
std::atomic<bool> flag;
};
В первом случае flag
будет инициализирован значением true
ещё до начала исполнения функции конструктора.
Во втором случае на момент начала исполнения функции конструктора flag
не будет инициализирован. Уже в самой функции вызывается метод store
, который сохраняет переданное значение в flag
(что эквивалентно flag = true;
).
Правка:
std::atomic_init(&flag, true);// 3
Это сработает. Однако, в документации указаны три предупреждения о том, что эта функция не является атомарной и одновременный доступ из разных потоков приведёт к состоянию гонки, даже если такой доступ будет осуществлён посредством атомарной операции.
Кроме этого, поведение будет неопределённым, если объект не был инициализирован по умолчанию, или если эта функция будет вызвана дважды для одного и того же объекта.
Первый способ даёт возможность безопасно инициализировать flag
, без опасений вызвать неопределённое поведение, поэтому, на мой взгляд, atomic_init
не стоит использовать в вашем случае.
Правка №2:
Изучая документацию для std::atomic<T>
, я обнаружил что процесс инициализации атомарного типа std::atomic<T>
с помощью конструктора
constexpr atomic( T desired ) noexcept;
не является атомарной операцией:
2) Initializes the underlying value with desired. The initialization is not atomic.
В черновике стандарта C++ по этому поводу приведено замечание ([atomics.types.operations]
):
[Note: It is possible to have an access to an atomic object A race with its construction, for example by communicating the address of the just-constructed object A to another thread via memory_-order::relaxedoperations on a suitable atomic pointer variable, and then immediately accessing A in the receiving thread. This results in undefined behavior.— end note]
смысл которого в том, что присутствует возможность для возникновения состояния гонки между конструированием атомарного объекта и обращением к нему в том случае, когда указатель на только что созданный атомарный объект передаётся другому потоку исполнения, который сразу же обращается к этому объекту.
Поэтому, то, что я написал ранее
Первый способ даёт возможность безопасно инициализировать flag
не совсем верно.
Корректно было бы сказать, что с помощью первого способа гораздо сложнее наступить на грабли, по сравнению с третьим способом, приведённым вами (использование std::atomic_init
).
Если вы не передаёте указатель на flag
в другой поток исполнения до того как объект Object
создан, то использование первого способа является безопасным, и с атомарным объектом всё будет в порядке.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Вроде простейшая задача, но какая-то фигняСам код: