namespace std { template <class T> struct atomic { using value_type = T; static constexpr bool is_always_lock_free = implementation-defined; bool is_lock_free() const volatile noexcept; bool is_lock_free() const noexcept; void store(T, memory_order = memory_order_seq_cst) volatile noexcept; void store(T, memory_order = memory_order_seq_cst) noexcept; T load(memory_order = memory_order_seq_cst) const volatile noexcept; T load(memory_order = memory_order_seq_cst) const noexcept; operator T() const volatile noexcept; operator T() const noexcept; T exchange(T, memory_order = memory_order_seq_cst) volatile noexcept; T exchange(T, memory_order = memory_order_seq_cst) noexcept; bool compare_exchange_weak(T&, T, memory_order, memory_order) volatile noexcept; bool compare_exchange_weak(T&, T, memory_order, memory_order) noexcept; bool compare_exchange_strong(T&, T, memory_order, memory_order) volatile noexcept; bool compare_exchange_strong(T&, T, memory_order, memory_order) noexcept; bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst) volatile noexcept; bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst) noexcept; bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst) volatile noexcept; bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst) noexcept; atomic() noexcept = default; constexpr atomic(T) noexcept; atomic(const atomic&) = delete; atomic& operator=(const atomic&) = delete; atomic& operator=(const atomic&) volatile = delete; T operator=(T) volatile noexcept; T operator=(T) noexcept; }; }
Аргумент шаблона для T должен быть легко копируемым ([basic.types]). [ Note: Аргументы типа, которые также не могут быть статически инициализированы, могут быть трудными для использования. ] — end note
[ Note: Представление атомарной специализации не обязательно должно иметь тот же размер, что и соответствующий тип аргумента. По возможности, специализации должны иметь одинаковый размер, так как это сокращает усилия, необходимые для переноса существующего кода. ] — end note
[ Note: Многие операции квалифицируются как нестабильные. Семантика «изменчивый как регистр устройства» в стандарте не изменилась. Эта квалификация означает, что изменчивость сохраняется при применении этих операций к изменчивым объектам. Это не означает, что операции с энергонезависимыми объектами становятся нестабильными. ] — end note
atomic() noexcept = default;
Effects: Оставляет атомарный объект в неинициализированном состоянии. [ Note: Эта семантика обеспечивает совместимость с C. ] — end note
constexpr atomic(T desired) noexcept;
Effects: Инициализирует объект значениемdesired. Инициализация - это не атомарная операция ([intro.multithread]). [ Note: Можно получить доступ кA гонке атомарных объектов с ее построением, например, передав адрес только что созданного объектаA другому потоку с помощью memory_order_relaxed операций с подходящей переменной атомарного указателя, а затем немедленно обращаясьA к принимающему потоку. Это приводит к неопределенному поведению. ] — end note
#define ATOMIC_VAR_INIT(value) see below
Макрос расширяется до последовательности токенов, подходящей для постоянной инициализации атомарной переменной со статической продолжительностью хранения типа, совместимого с инициализациейvalue. [ Note: Эта операция может потребоваться для инициализации блокировок. ] Параллельный доступ к инициализируемой переменной, даже через атомарную операцию, составляет гонку данных. [ — end note Example:
atomic<int> v = ATOMIC_VAR_INIT(5);
— end example ]
static constexpr bool is_always_lock_free = implementation-defined;
static Элемент данныхis_always_lock_free является ,true если операции в атомарном типе являются всегда безблокировочными, и вfalse противном случае. [ Note: Значениеis_always_lock_free согласуется со значением соответствующегоATOMIC_..._LOCK_FREE макроса, если он определен. ] — end note
bool is_lock_free() const volatile noexcept;
bool is_lock_free() const noexcept;
Returns:true если операции объекта не блокируются, вfalse противном случае. [ Note: Возвращаемое значение функции-is_lock_free члена согласуется со значениемis_always_lock_free для того же типа. ] — end note
void store(T desired, memory_order order = memory_order_seq_cst) volatile noexcept;
void store(T desired, memory_order order = memory_order_seq_cst) noexcept;
Requires: order Аргумент не может бытьmemory_order_consume, memory_order_acquireи неmemory_order_acq_rel.
Effects: Атомно заменяет значение, на которое указывает,this на значениеdesired. На память влияет значение order.
T operator=(T desired) volatile noexcept;
T operator=(T desired) noexcept;
T load(memory_order order = memory_order_seq_cst) const volatile noexcept;
T load(memory_order order = memory_order_seq_cst) const noexcept;
operator T() const volatile noexcept;
operator T() const noexcept;
T exchange(T desired, memory_order order = memory_order_seq_cst) volatile noexcept;
T exchange(T desired, memory_order order = memory_order_seq_cst) noexcept;
Effects: Атомно заменяет значение , на который указываетthis сdesired. На память влияет значениеorder. Эти операции являются атомарными операциями чтения-изменения-записи ([intro.multithread]).
bool compare_exchange_weak(T& expected, T desired,
memory_order success, memory_order failure) volatile noexcept;
bool compare_exchange_weak(T& expected, T desired,
memory_order success, memory_order failure) noexcept;
bool compare_exchange_strong(T& expected, T desired,
memory_order success, memory_order failure) volatile noexcept;
bool compare_exchange_strong(T& expected, T desired,
memory_order success, memory_order failure) noexcept;
bool compare_exchange_weak(T& expected, T desired,
memory_order order = memory_order_seq_cst) volatile noexcept;
bool compare_exchange_weak(T& expected, T desired,
memory_order order = memory_order_seq_cst) noexcept;
bool compare_exchange_strong(T& expected, T desired,
memory_order order = memory_order_seq_cst) volatile noexcept;
bool compare_exchange_strong(T& expected, T desired,
memory_order order = memory_order_seq_cst) noexcept;
Effects: Извлекает значение в форматеexpected. Затем он атомарно сравнивает содержимое памяти, на которую указываетthis на равенство с тем, что было ранее получено изexpected, и, если истинно, заменяет содержимое памяти, на которое указывает, на то,this что вdesired. Если и только если сравнение истинно, память затрагивается в соответствии со значениемsuccess, а если сравнение ложно, память затрагивается в соответствии со значениемfailure. Когда предоставляется только одинmemory_order аргумент, значениеsuccess равноorder, а значение failure равно,order за исключением того, что значениеmemory_order_acq_rel должно быть заменено значением,memory_order_acquire а значение memory_order_release должно быть заменено значением memory_order_relaxed. Если и только если сравнение ложно, то после атомарной операции содержимое памяти вexpected заменяется значением, считанным из памяти, на которую указываетthis во время атомарного сравнения. Если операция возвращаетсяtrue, эти операции являются атомарными операциями чтения-изменения-записи ([intro.multithread]) в памяти, на которую указываетthis. В противном случае эти операции представляют собой операции атомарной загрузки в эту память.
[ Note: Например, эффект compare_exchange_strong есть
if (memcmp(this, &expected, sizeof(*this)) == 0) memcpy(this, &desired, sizeof(*this)); else memcpy(expected, this, sizeof(*this));
— end note ] [ Example: Ожидаемое использование операций сравнения и обмена следующее. Операции сравнения и обмена обновятся,expected когда потребуется еще одна итерация цикла.
expected = current.load(); do { desired = function(expected); } while (!current.compare_exchange_weak(expected, desired));
— end example ] [ Example: Поскольку ожидаемое значение обновляется только в случае ошибки, код, освобождающий память, содержащуюexpected значение, в случае успеха будет работать. Например, вставка заголовка списка будет действовать атомарно и не приведет к гонке данных в следующем коде:
do { p->next = head; // make new list node point to the current head } while (!head.compare_exchange_weak(p->next, p)); // try to insert
— end example ]
Реализации должны гарантировать, что слабые операции сравнения и обмена не будут последовательно возвращаться,false если только атомарный объект не имеет значение, отличное от атомарного объекта,expected или не происходит одновременных модификаций атомарного объекта.
Remarks: Слабая операция сравнения и обмена может привести к ложному сбою. То есть, даже когда содержимое памяти, на которое ссылаетсяexpected иthis равны, оно может вернутьсяfalse и сохранить обратно вexpected то же содержимое памяти, которое было изначально там. [ Note: Этот ложный сбой позволяет реализовать сравнение и обмен на более широком классе машин, например, на машинах с фиксированной загрузкой и условием сохранения. Следствием ложного отказа является то, что почти все случаи использования слабого сравнения и обмена будут замкнутыми.
Когда в цикле сравнения и обмена, слабая версия даст лучшую производительность на некоторых платформах. Когда для слабого сравнения и обмена требуется петля, а для сильной - нет, предпочтительнее сильное. ] — end note
[ Note: Вmemcpy иmemcmp семантике операций сравнения и обмена-может привести к неудачным сравнениям для значений , которые сравнивают с равным ,operator== если основным типа имеет биты заполнения, ловушки биты или альтернативные представления одного и то же значение. Таким образом,compare_exchange_strong следует использовать с особой осторожностью. С другой стороны,compare_exchange_weak должно быстро сходиться. ] — end note
Есть специализацииatomic шаблона для целочисленных типов char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, char16_t, char32_t, wchar_t, и любых другие типов необходимо в определениях типов в заголовке<cstdint>. Для каждого такого интегрального типаintegralспециализация atomic<integral> предоставляет дополнительные атомарные операции, соответствующие целочисленным типам. [ Note: О специализацииatomic<bool>см[atomics.types.generic]. ] — end note
namespace std { template <> struct atomic<integral> { using value_type = integral; using difference_type = value_type; static constexpr bool is_always_lock_free = implementation-defined; bool is_lock_free() const volatile noexcept; bool is_lock_free() const noexcept; void store(integral, memory_order = memory_order_seq_cst) volatile noexcept; void store(integral, memory_order = memory_order_seq_cst) noexcept; integral load(memory_order = memory_order_seq_cst) const volatile noexcept; integral load(memory_order = memory_order_seq_cst) const noexcept; operator integral() const volatile noexcept; operator integral() const noexcept; integral exchange(integral, memory_order = memory_order_seq_cst) volatile noexcept; integral exchange(integral, memory_order = memory_order_seq_cst) noexcept; bool compare_exchange_weak(integral&, integral, memory_order, memory_order) volatile noexcept; bool compare_exchange_weak(integral&, integral, memory_order, memory_order) noexcept; bool compare_exchange_strong(integral&, integral, memory_order, memory_order) volatile noexcept; bool compare_exchange_strong(integral&, integral, memory_order, memory_order) noexcept; bool compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst) volatile noexcept; bool compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst) noexcept; bool compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst) volatile noexcept; bool compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst) noexcept; integral fetch_add(integral, memory_order = memory_order_seq_cst) volatile noexcept; integral fetch_add(integral, memory_order = memory_order_seq_cst) noexcept; integral fetch_sub(integral, memory_order = memory_order_seq_cst) volatile noexcept; integral fetch_sub(integral, memory_order = memory_order_seq_cst) noexcept; integral fetch_and(integral, memory_order = memory_order_seq_cst) volatile noexcept; integral fetch_and(integral, memory_order = memory_order_seq_cst) noexcept; integral fetch_or(integral, memory_order = memory_order_seq_cst) volatile noexcept; integral fetch_or(integral, memory_order = memory_order_seq_cst) noexcept; integral fetch_xor(integral, memory_order = memory_order_seq_cst) volatile noexcept; integral fetch_xor(integral, memory_order = memory_order_seq_cst) noexcept; atomic() noexcept = default; constexpr atomic(integral) noexcept; atomic(const atomic&) = delete; atomic& operator=(const atomic&) = delete; atomic& operator=(const atomic&) volatile = delete; integral operator=(integral) volatile noexcept; integral operator=(integral) noexcept; integral operator++(int) volatile noexcept; integral operator++(int) noexcept; integral operator--(int) volatile noexcept; integral operator--(int) noexcept; integral operator++() volatile noexcept; integral operator++() noexcept; integral operator--() volatile noexcept; integral operator--() noexcept; integral operator+=(integral) volatile noexcept; integral operator+=(integral) noexcept; integral operator-=(integral) volatile noexcept; integral operator-=(integral) noexcept; integral operator&=(integral) volatile noexcept; integral operator&=(integral) noexcept; integral operator|=(integral) volatile noexcept; integral operator|=(integral) noexcept; integral operator^=(integral) volatile noexcept; integral operator^=(integral) noexcept; }; }
Атомарные интегральные специализации представляют собой стандартные структуры. У каждого из них есть тривиальный конструктор по умолчанию и тривиальный деструктор.
Следующие операции выполняют арифметические вычисления. Соответствие ключа, оператора и вычислений:
key | Op | Вычисление | key | Op | Вычисление |
add | + | добавление | sub | - | вычитание |
or | | | побитовое включение или | xor | ^ | побитовое исключающее или |
and | & | побитовое и |
T fetch_key(T operand, memory_order order = memory_order_seq_cst) volatile noexcept;
T fetch_key(T operand, memory_order order = memory_order_seq_cst) noexcept;
Effects: Атомно заменяет значение, на которое указывает, this результатом вычисления, примененного к значению, на которое указываетthis и данноеoperand. На память влияет значениеorder. Эти операции являются атомарными операциями чтения-изменения-записи ([intro.multithread]).
T operator op=(T operand) volatile noexcept;
T operator op=(T operand) noexcept;
namespace std { template <class T> struct atomic<T*> { using value_type = T*; using difference_type = ptrdiff_t; static constexpr bool is_always_lock_free = implementation-defined; bool is_lock_free() const volatile noexcept; bool is_lock_free() const noexcept; void store(T*, memory_order = memory_order_seq_cst) volatile noexcept; void store(T*, memory_order = memory_order_seq_cst) noexcept; T* load(memory_order = memory_order_seq_cst) const volatile noexcept; T* load(memory_order = memory_order_seq_cst) const noexcept; operator T*() const volatile noexcept; operator T*() const noexcept; T* exchange(T*, memory_order = memory_order_seq_cst) volatile noexcept; T* exchange(T*, memory_order = memory_order_seq_cst) noexcept; bool compare_exchange_weak(T*&, T*, memory_order, memory_order) volatile noexcept; bool compare_exchange_weak(T*&, T*, memory_order, memory_order) noexcept; bool compare_exchange_strong(T*&, T*, memory_order, memory_order) volatile noexcept; bool compare_exchange_strong(T*&, T*, memory_order, memory_order) noexcept; bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst) volatile noexcept; bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst) noexcept; bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst) volatile noexcept; bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst) noexcept; T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst) volatile noexcept; T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst) noexcept; T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst) volatile noexcept; T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst) noexcept; atomic() noexcept = default; constexpr atomic(T*) noexcept; atomic(const atomic&) = delete; atomic& operator=(const atomic&) = delete; atomic& operator=(const atomic&) volatile = delete; T* operator=(T*) volatile noexcept; T* operator=(T*) noexcept; T* operator++(int) volatile noexcept; T* operator++(int) noexcept; T* operator--(int) volatile noexcept; T* operator--(int) noexcept; T* operator++() volatile noexcept; T* operator++() noexcept; T* operator--() volatile noexcept; T* operator--() noexcept; T* operator+=(ptrdiff_t) volatile noexcept; T* operator+=(ptrdiff_t) noexcept; T* operator-=(ptrdiff_t) volatile noexcept; T* operator-=(ptrdiff_t) noexcept; }; }
Имеется частичная специализацияatomic шаблона класса для указателей. Специализации этой частичной специализации - это структуры стандартного макета. У каждого из них есть тривиальный конструктор по умолчанию и тривиальный деструктор.
Следующие операции выполняют арифметические действия с указателями. Соответствие ключа, оператора и вычислений:
Key | Op | Вычисление | Key | Op | Вычисление |
add | + | добавление | sub | - | вычитание |
T* fetch_key(ptrdiff_t operand, memory_order order = memory_order_seq_cst) volatile noexcept;
T* fetch_key(ptrdiff_t operand, memory_order order = memory_order_seq_cst) noexcept;
Requires: T должен быть типом объекта, иначе программа будет некорректной. [ Note: Арифметика указателей наvoid* указатели функций неправильно сформирована. ] — end note
Effects: Атомно заменяет значение, на которое указывает, this результатом вычисления, примененного к значению, на которое указываетthis и данноеoperand. На память влияет значениеorder. Эти операции являются атомарными операциями чтения-изменения-записи ([intro.multithread]).
Remarks: Результатом может быть неопределенный адрес, но в противном случае операции не имеют неопределенного поведения.
T* operator op=(ptrdiff_t operand) volatile noexcept;
T* operator op=(ptrdiff_t operand) noexcept;
T operator++(int) volatile noexcept;
T operator++(int) noexcept;
Effects: Эквивалентен:return fetchadd(1);
T operator--(int) volatile noexcept;
T operator--(int) noexcept;
Effects: Эквивалентен:return fetchsub(1);
T operator++() volatile noexcept;
T operator++() noexcept;
T operator--() volatile noexcept;
T operator--() noexcept;