В этом разделе описаны компоненты для детального атомарного доступа. Этот доступ предоставляется через операции с атомарными объектами.
В следующих подпунктах описываются требования к атомарности и компоненты для типов и операций, как кратко изложено ниже.
Подпункт | Заголовок (ы) | |
[atomics.order] | Порядок и последовательность | |
[atomics.lockfree] | Замок-свободная собственность | |
[atomics.types.generic] | Атомные типы | <atomic> |
[atomics.types.operations] | Операции с атомными типами | |
[atomics.flag] | Тип флага и операции | |
[atomics.fences] | Заборы |
namespace std { // [atomics.order], order and consistency enum memory_order; template <class T> T kill_dependency(T y) noexcept; // [atomics.lockfree], lock-free property #define ATOMIC_BOOL_LOCK_FREE unspecified #define ATOMIC_CHAR_LOCK_FREE unspecified #define ATOMIC_CHAR16_T_LOCK_FREE unspecified #define ATOMIC_CHAR32_T_LOCK_FREE unspecified #define ATOMIC_WCHAR_T_LOCK_FREE unspecified #define ATOMIC_SHORT_LOCK_FREE unspecified #define ATOMIC_INT_LOCK_FREE unspecified #define ATOMIC_LONG_LOCK_FREE unspecified #define ATOMIC_LLONG_LOCK_FREE unspecified #define ATOMIC_POINTER_LOCK_FREE unspecified // [atomics.types.generic], atomic template<class T> struct atomic; // [atomics.types.pointer], partial specialization for pointers template<class T> struct atomic<T*>; // [atomics.nonmembers], non-member functions template<class T> bool atomic_is_lock_free(const volatile atomic<T>*) noexcept; template<class T> bool atomic_is_lock_free(const atomic<T>*) noexcept; template<class T> void atomic_init(volatile atomic<T>*, typename atomic<T>::value_type) noexcept; template<class T> void atomic_init(atomic<T>*, typename atomic<T>::value_type) noexcept; template<class T> void atomic_store(volatile atomic<T>*, typename atomic<T>::value_type) noexcept; template<class T> void atomic_store(atomic<T>*, typename atomic<T>::value_type) noexcept; template<class T> void atomic_store_explicit(volatile atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template<class T> void atomic_store_explicit(atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template<class T> T atomic_load(const volatile atomic<T>*) noexcept; template<class T> T atomic_load(const atomic<T>*) noexcept; template<class T> T atomic_load_explicit(const volatile atomic<T>*, memory_order) noexcept; template<class T> T atomic_load_explicit(const atomic<T>*, memory_order) noexcept; template<class T> T atomic_exchange(volatile atomic<T>*, T) noexcept; template<class T> T atomic_exchange(atomic<T>*, typename atomic<T>::value_type) noexcept; template<class T> T atomic_exchange_explicit(volatile atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template<class T> T atomic_exchange_explicit(atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template<class T> bool atomic_compare_exchange_weak(volatile atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type) noexcept; template<class T> bool atomic_compare_exchange_weak(atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type) noexcept; template<class T> bool atomic_compare_exchange_strong(volatile atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type) noexcept; template<class T> bool atomic_compare_exchange_strong(atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type) noexcept; template<class T> bool atomic_compare_exchange_weak_explicit(volatile atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_weak_explicit(atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_strong_explicit(volatile atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_strong_explicit(atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type, memory_order, memory_order) noexcept; template <class T> T atomic_fetch_add(volatile atomic<T>*, typename atomic<T>::difference_type) noexcept; template <class T> T atomic_fetch_add(atomic<T>*, typename atomic<T>::difference_type) noexcept; template <class T> T atomic_fetch_add_explicit(volatile atomic<T>*, typename atomic<T>::difference_type, memory_order) noexcept; template <class T> T atomic_fetch_add_explicit(atomic<T>*, typename atomic<T>::difference_type, memory_order) noexcept; template <class T> T atomic_fetch_sub(volatile atomic<T>*, typename atomic<T>::difference_type) noexcept; template <class T> T atomic_fetch_sub(atomic<T>*, typename atomic<T>::difference_type) noexcept; template <class T> T atomic_fetch_sub_explicit(volatile atomic<T>*, typename atomic<T>::difference_type, memory_order) noexcept; template <class T> T atomic_fetch_sub_explicit(atomic<T>*, typename atomic<T>::difference_type, memory_order) noexcept; template <class T> T atomic_fetch_and(volatile atomic<T>*, typename atomic<T>::value_type) noexcept; template <class T> T atomic_fetch_and(atomic<T>*, typename atomic<T>::value_type) noexcept; template <class T> T atomic_fetch_and_explicit(volatile atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template <class T> T atomic_fetch_and_explicit(atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template <class T> T atomic_fetch_or(volatile atomic<T>*, typename atomic<T>::value_type) noexcept; template <class T> T atomic_fetch_or(atomic<T>*, typename atomic<T>::value_type) noexcept; template <class T> T atomic_fetch_or_explicit(volatile atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template <class T> T atomic_fetch_or_explicit(atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template <class T> T atomic_fetch_xor(volatile atomic<T>*, typename atomic<T>::value_type) noexcept; template <class T> T atomic_fetch_xor(atomic<T>*, typename atomic<T>::value_type) noexcept; template <class T> T atomic_fetch_xor_explicit(volatile atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template <class T> T atomic_fetch_xor_explicit(atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; // [atomics.types.operations], initialization #define ATOMIC_VAR_INIT(value) see below // [atomics.alias], type aliases using atomic_bool = atomic<bool>; using atomic_char = atomic<char>; using atomic_schar = atomic<signed char>; using atomic_uchar = atomic<unsigned char>; using atomic_short = atomic<short>; using atomic_ushort = atomic<unsigned short>; using atomic_int = atomic<int>; using atomic_uint = atomic<unsigned int>; using atomic_long = atomic<long>; using atomic_ulong = atomic<unsigned long>; using atomic_llong = atomic<long long>; using atomic_ullong = atomic<unsigned long long>; using atomic_char16_t = atomic<char16_t>; using atomic_char32_t = atomic<char32_t>; using atomic_wchar_t = atomic<wchar_t>; using atomic_int8_t = atomic<int8_t>; using atomic_uint8_t = atomic<uint8_t>; using atomic_int16_t = atomic<int16_t>; using atomic_uint16_t = atomic<uint16_t>; using atomic_int32_t = atomic<int32_t>; using atomic_uint32_t = atomic<uint32_t>; using atomic_int64_t = atomic<int64_t>; using atomic_uint64_t = atomic<uint64_t>; using atomic_int_least8_t = atomic<int_least8_t>; using atomic_uint_least8_t = atomic<uint_least8_t>; using atomic_int_least16_t = atomic<int_least16_t>; using atomic_uint_least16_t = atomic<uint_least16_t>; using atomic_int_least32_t = atomic<int_least32_t>; using atomic_uint_least32_t = atomic<uint_least32_t>; using atomic_int_least64_t = atomic<int_least64_t>; using atomic_uint_least64_t = atomic<uint_least64_t>; using atomic_int_fast8_t = atomic<int_fast8_t>; using atomic_uint_fast8_t = atomic<uint_fast8_t>; using atomic_int_fast16_t = atomic<int_fast16_t>; using atomic_uint_fast16_t = atomic<uint_fast16_t>; using atomic_int_fast32_t = atomic<int_fast32_t>; using atomic_uint_fast32_t = atomic<uint_fast32_t>; using atomic_int_fast64_t = atomic<int_fast64_t>; using atomic_uint_fast64_t = atomic<uint_fast64_t>; using atomic_intptr_t = atomic<intptr_t>; using atomic_uintptr_t = atomic<uintptr_t>; using atomic_size_t = atomic<size_t>; using atomic_ptrdiff_t = atomic<ptrdiff_t>; using atomic_intmax_t = atomic<intmax_t>; using atomic_uintmax_t = atomic<uintmax_t>; // [atomics.flag], flag type and operations struct atomic_flag; bool atomic_flag_test_and_set(volatile atomic_flag*) noexcept; bool atomic_flag_test_and_set(atomic_flag*) noexcept; bool atomic_flag_test_and_set_explicit(volatile atomic_flag*, memory_order) noexcept; bool atomic_flag_test_and_set_explicit(atomic_flag*, memory_order) noexcept; void atomic_flag_clear(volatile atomic_flag*) noexcept; void atomic_flag_clear(atomic_flag*) noexcept; void atomic_flag_clear_explicit(volatile atomic_flag*, memory_order) noexcept; void atomic_flag_clear_explicit(atomic_flag*, memory_order) noexcept; #define ATOMIC_FLAG_INIT see below // [atomics.fences], fences extern "C" void atomic_thread_fence(memory_order) noexcept; extern "C" void atomic_signal_fence(memory_order) noexcept; }
namespace std { enum memory_order { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst }; }
Перечисление memory_order определяет подробный регулярный (неатомарный) порядок синхронизации памяти, как определено в, [intro.multithread] и может предусматривать порядок операций. Его перечисляемые значения и их значения следующие:
memory_order_relaxed: никакая операция не заказывает память.
memory_order_release, memory_order_acq_relи memory_order_seq_cst: операция сохранения выполняет операцию освобождения затронутой области памяти.
memory_order_consume: операция загрузки выполняет операцию потребления в затронутой области памяти. [ Note: Предпочтение memory_order_acquire, которое обеспечивает более сильные гарантии, чем memory_order_consume. Реализации сочли невозможным обеспечить производительность лучше, чем у memory_order_acquire. Пересмотр спецификации находится на рассмотрении. ] — end note
memory_order_acquire, memory_order_acq_relи memory_order_seq_cst: операция загрузки выполняет операцию получения в затронутой области памяти.
[ Note: Указание атомарных операций memory_order_relaxed ослаблено в отношении упорядочения памяти. Реализации должны по-прежнему гарантировать, что любой заданный атомарный доступ к конкретному атомарному объекту будет неделимым по отношению ко всем другим атомарным доступам к этому объекту. ] — end note
Атомарная операция, A которая выполняет операцию выпуска над атомарным объектом, M синхронизируется с атомарной операцией, B которая выполняет операцию получения M и берет свое значение из любого побочного эффекта в последовательности выпуска, озаглавленной A.
Должен быть единый общий порядок S для всех memory_order_seq_cst операций, соответствующий порядку «происходит до» и порядкам модификации для всех затронутых местоположений, так что каждая memory_order_seq_cst операция, B которая загружает значение из атомарного объекта, M наблюдает одно из следующих значений:
результат последней модификации A из того, M что предшествует B в S, если она существует, или
если A существует, результат некоторой модификации M этого не происходит memory_order_seq_cst и не происходило раньше A, или
если A не существует, результат некоторой модификации M этого не существует memory_order_seq_cst.
[ Note: Хотя явно не требуется S включать блокировки, его всегда можно расширить до порядка, который включает операции блокировки и разблокировки, поскольку упорядочение между ними уже включено в порядок «происходит до». ] — end note
Для атомарной операции, B которая считывает значение атомарного объекта M, если есть memory_order_seq_cst забор, X упорядоченный ранее B, то B наблюдает либо последнюю memory_order_seq_cst модификацию M предыдущего X в общем порядке, S либо более позднюю модификацию M в его порядке модификации.
Для атомарных операций A и B для атомарного объекта M, где A модифицирует M и B принимает свое значение, если есть memory_order_seq_cst ограждение X , которое A упорядочено до X и B следует X за ним S, то B наблюдает либо эффекты, A либо последующие модификации M в порядке его модификации.
Для атомарных операций A и B на атомарном объекте M, где A модифицирует M и B принимает свое значение, если есть memory_order_seq_cst ограждения, X и Y такое, что A упорядочено до X, Y упорядочено до Bи X предшествует Y в S, то B наблюдает либо эффекты, A либо более позднюю модификацию M в порядке его модификации .
Для атомных модификаций A и B атомного объекта M, B происходит позже , чем A в порядке модификации , M если:
есть memory_order_seq_cst забор X таким образом, что A секвенировали до того X, и X предшествует B в S, или
есть memory_order_seq_cst забор Y таким образом, что Y секвенировали до того B, и A предшествует Y в S, или
есть memory_order_seq_cst заборы X и Y такие, что A упорядочены до X, Y упорядочены до Bи X предшествуют Y в S.
[ Note: memory_order_seq_cst обеспечивает последовательную согласованность только для программы, которая свободна от гонок данных и использует исключительно memory_order_seq_cst операции. Любое использование более слабого порядка приведет к аннулированию этой гарантии, если не будут приняты особые меры предосторожности. В частности, memory_order_seq_cst заборы обеспечивают полный порядок только самих заборов. Как правило, ограждения не могут использоваться для восстановления последовательной согласованности атомарных операций с более слабыми спецификациями упорядочивания. ] — end note
Реализации должны гарантировать, что не вычисляются «неожиданные» значения, которые циклически зависят от их собственных вычислений.
[ Note: Например, x и y изначально равна нулю,
// Thread 1:
r1 = y.load(memory_order_relaxed);
x.store(r1, memory_order_relaxed);
// Thread 2:
r2 = x.load(memory_order_relaxed);
y.store(r2, memory_order_relaxed);
не следует производить r1 == r2 == 42, так как хранение от 42 до y возможно только при хранении в x магазинах 42, которое циклически зависит от магазина для y хранения 42. Обратите внимание, что без этого ограничения такое выполнение возможно. ] — end note
[ Note: Рекомендация аналогичным образом запрещает r1 == r2 == 42 в следующем примере с снова x и y снова с нулевым значением:
// Thread 1:
r1 = x.load(memory_order_relaxed);
if (r1 == 42) y.store(42, memory_order_relaxed);
// Thread 2:
r2 = y.load(memory_order_relaxed);
if (r2 == 42) x.store(42, memory_order_relaxed);
— end note ]
Атомарные операции чтения-изменения-записи всегда должны считывать последнее значение (в порядке модификации), записанное перед записью, связанной с операцией чтения-изменения-записи.
Реализации должны делать атомарные хранилища видимыми для атомарных загрузок в течение разумного промежутка времени.
template <class T>
T kill_dependency(T y) noexcept;
Effects: Аргумент не carry a dependency соответствует возвращаемому значению.
#define ATOMIC_BOOL_LOCK_FREE unspecified #define ATOMIC_CHAR_LOCK_FREE unspecified #define ATOMIC_CHAR16_T_LOCK_FREE unspecified #define ATOMIC_CHAR32_T_LOCK_FREE unspecified #define ATOMIC_WCHAR_T_LOCK_FREE unspecified #define ATOMIC_SHORT_LOCK_FREE unspecified #define ATOMIC_INT_LOCK_FREE unspecified #define ATOMIC_LONG_LOCK_FREE unspecified #define ATOMIC_LLONG_LOCK_FREE unspecified #define ATOMIC_POINTER_LOCK_FREE unspecified
В ATOMIC_..._LOCK_FREE макросы указывают свойство блокировки свободной от соответствующих атомарных типов, с подписанные и неподписанные варианты сгруппированы вместе. Свойства также применяются к соответствующим (частичным) специализациям atomic шаблона. Значение 0 указывает, что типы никогда не освобождаются от блокировки. Значение 1 указывает, что типы иногда не блокируются. Значение 2 указывает, что типы всегда свободны от блокировок.
Функция atomic_is_lock_free указывает, свободен ли объект от блокировки. При выполнении любой данной программы результат запроса без блокировки должен быть согласован для всех указателей одного и того же типа.
Атомарные операции, которые не являются свободными от блокировок, считаются потенциально block ([intro.progress]).
[ Note: Операции без блокировки также должны быть безадресными. То есть атомарные операции в одном и том же месте памяти через два разных адреса будут взаимодействовать атомарно. Реализация не должна зависеть от состояния каждого процесса. Это ограничение позволяет осуществлять обмен данными с помощью памяти, которая отображается на процесс более одного раза, и с помощью памяти, которая используется совместно двумя процессами. ] — end note
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]).
Returns: Атомарно возвращает значение, на которое указывает this непосредственно перед эффектами.
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;
Шаблон функции, не являющейся членом, имя которой совпадает с шаблоном atomic_f или шаблоном, atomic_f_explicit вызывает функцию-член fсо значением первого параметра в качестве выражения объекта и значениями остальных параметров (если есть) в качестве аргументов вызова функции-члена, чтобы. Аргумент для параметра типа atomic<T>::value_type* разыменовывается при передаче в вызов функции-члена. Если такой функции-члена не существует, программа имеет неправильный формат.
template<class T>
void atomic_init(volatile atomic<T>* object, typename atomic<T>::value_type desired) noexcept;
template<class T>
void atomic_init(atomic<T>* object, typename atomic<T>::value_type desired) noexcept;
Effects: Неатомарно инициализируется *object значением desired. Эта функция должна применяться только к объектам, которые были созданы по умолчанию, и только один раз. [ Note: Эта семантика обеспечивает совместимость с C. ] [ Параллельный доступ из другого потока, даже через атомарную операцию, составляет гонку данных. ] — end note Note: — end note
namespace std {
struct atomic_flag {
bool test_and_set(memory_order = memory_order_seq_cst) volatile noexcept;
bool test_and_set(memory_order = memory_order_seq_cst) noexcept;
void clear(memory_order = memory_order_seq_cst) volatile noexcept;
void clear(memory_order = memory_order_seq_cst) noexcept;
atomic_flag() noexcept = default;
atomic_flag(const atomic_flag&) = delete;
atomic_flag& operator=(const atomic_flag&) = delete;
atomic_flag& operator=(const atomic_flag&) volatile = delete;
};
bool atomic_flag_test_and_set(volatile atomic_flag*) noexcept;
bool atomic_flag_test_and_set(atomic_flag*) noexcept;
bool atomic_flag_test_and_set_explicit(volatile atomic_flag*, memory_order) noexcept;
bool atomic_flag_test_and_set_explicit(atomic_flag*, memory_order) noexcept;
void atomic_flag_clear(volatile atomic_flag*) noexcept;
void atomic_flag_clear(atomic_flag*) noexcept;
void atomic_flag_clear_explicit(volatile atomic_flag*, memory_order) noexcept;
void atomic_flag_clear_explicit(atomic_flag*, memory_order) noexcept;
#define ATOMIC_FLAG_INIT see below
}
atomic_flag Тип обеспечивает классическую функциональность тест-и-набор. Он имеет два состояния: установленное и очищенное.
Операции с объектом типа atomic_flag не должны блокироваться. [ Note: Следовательно, операции также должны быть безадресными. ] — end note
atomic_flag Типом является стандартной компоновкой структуры. Он имеет тривиальный конструктор по умолчанию и тривиальный деструктор.
Макрос ATOMIC_FLAG_INIT должен быть определен таким образом, чтобы его можно было использовать для инициализации объекта типа atomic_flag в чистое состояние. Макрос можно использовать в виде:
atomic_flag guard = ATOMIC_FLAG_INIT;
Не указано, можно ли использовать макрос в других контекстах инициализации. Для полного объекта статической продолжительности эта инициализация должна быть статической. Если не инициализирован с помощью ATOMIC_FLAG_INIT, не указано,atomic_flag имеет ли объект начальное состояние set или clear.
bool atomic_flag_test_and_set(volatile atomic_flag* object) noexcept;
bool atomic_flag_test_and_set(atomic_flag* object) noexcept;
bool atomic_flag_test_and_set_explicit(volatile atomic_flag* object, memory_order order) noexcept;
bool atomic_flag_test_and_set_explicit(atomic_flag* object, memory_order order) noexcept;
bool atomic_flag::test_and_set(memory_order order = memory_order_seq_cst) volatile noexcept;
bool atomic_flag::test_and_set(memory_order order = memory_order_seq_cst) noexcept;
Effects: Атомно устанавливает значение, на которое указывает object или указывает this на true. На память влияет значение order. Эти операции являются атомарными операциями чтения-изменения-записи ([intro.multithread]).
void atomic_flag_clear(volatile atomic_flag* object) noexcept;
void atomic_flag_clear(atomic_flag* object) noexcept;
void atomic_flag_clear_explicit(volatile atomic_flag* object, memory_order order) noexcept;
void atomic_flag_clear_explicit(atomic_flag* object, memory_order order) noexcept;
void atomic_flag::clear(memory_order order = memory_order_seq_cst) volatile noexcept;
void atomic_flag::clear(memory_order order = memory_order_seq_cst) noexcept;
Requires: order Аргумент не может быть memory_order_consume, memory_order_acquireи не memory_order_acq_rel.
В этом разделе представлены примитивы синхронизации, называемые fences. Ограничения могут иметь семантику приобретения, семантику выпуска или и то, и другое. Забор с семантикой приобретения называется acquire fence. Забор с семантикой выпуска называется файлом release fence.
Ограничение выпуска A синхронизируется с ограничением получения, B если существуют атомарные операции, X и Yоба они работают с некоторым атомарным объектом M, таким, который A упорядочивается раньше X, X модифицируется M, Y упорядочивается раньше Bи Y считывает значение, записанное X или значение, записанное любым побочным эффектом в Гипотетическая последовательность выпуска X могла бы начаться, если бы это была операция выпуска.
Ограничение выпуска A синхронизируется с атомарной операцией, B которая выполняет операцию получения на атомарном объекте, M если существует такая атомарная операция X , которая A упорядочена раньше X, X изменяет Mи B считывает значение, записанное X или значение, записанное любым побочным эффектом в гипотетической последовательности выпуска. X возглавил бы, если бы это была операция по освобождению.
Атомарная операция , A которая является операцией выпуска на атомном объекте M синхронизируется с приобретает забор , B если существует некоторая атомарная операция X на M таким образом, что X секвенируют до того, B и считывает значение , написанное A или значение , записанное с помощью какого - либо побочного эффекта в последовательности высвобождения во главе A.
extern "C" void atomic_thread_fence(memory_order order) noexcept;
Effects: В зависимости от значения orderэта операция:
не имеет эффекта, если order == memory_order_relaxed;
это приобретаемый забор, если order == memory_order_acquire || order == memory_order_consume;
это ограждение выпуска, если order == memory_order_release;
одновременно является забором для приобретения и забором для выпуска, если order == memory_order_acq_rel;
является последовательным последовательным захватом и освобождением, если order == memory_order_seq_cst.
extern "C" void atomic_signal_fence(memory_order order) noexcept;
Effects: Эквивалентно atomic_thread_fence(order), за исключением того, что результирующие ограничения порядка устанавливаются только между потоком и обработчиком сигнала, выполняемым в одном потоке.
[ Note: atomic_signal_fence может использоваться для указания порядка, в котором действия, выполняемые потоком, становятся видимыми для обработчика сигнала. Оптимизация компилятора и изменение порядка загрузки и сохранения запрещены таким же образом, как и с atomic_thread_fence, но инструкции аппаратного ограничения, atomic_thread_fence которые должны были быть вставлены, не выдаются. ] — end note