Таблица 5.2.Соответствие между стандартными атомарными и встроенными typedef
Атомарный typedef |
Соответствующий typedef из стандартной библиотеки |
atomic_int_least8_t |
int_least8_t |
atomic_uint_least8_t |
uint_least8_t |
atomic_int_least16_t |
int_least16_t |
atomic_uint_least16_t |
uint_least16_t |
atomic_int_least32_t |
int_least32_t |
atomic_uint_least32_t |
uint_least32_t |
atomic_int_least64_t |
int_least64_t |
atomic_uint_least64_t |
uint_least64_t |
atomic_int_fast8_t |
int_fast8_t |
atomic_uint_fast8_t |
uint_fast8_t |
atomic_int_fast16_t |
int_fast16_t |
atomic_uint_fast16_t |
uint_fast16_t |
atomic_int_fast32_t |
int_fast32_t |
atomic_uint_fast32_t |
uint_fast32_t |
atomic_int_fast64_t |
int_fast64_t |
atomic_uint_fast64_t |
uint_fast64_t |
atomic_intptr_t |
intptr_t |
atomic_uintptr_t |
uintptr_t |
atomic_size_t |
size_t |
atomic_ptrdiff_t |
ptrdiff_t |
atomic_intmax_t |
intmax_t |
atomic_uintmax_t |
uintmax_t |
Да уж, типов немало! Но есть простая закономерность — атомарный тип, соответствующий стандартному typedef T
, имеет такое же имя с префиксом atomic_
: atomic_T
. То же самое относится и к встроенным типам с тем исключением, что signed
сокращается до s
, unsigned
— до u
, a long long
— до llong
. Вообще говоря, проще написать std::atomic
для нужного вам типа T
, чем пользоваться альтернативными именами.
Стандартные атомарные типы не допускают копирования и присваивания в обычном смысле, то есть не имеют копирующих конструкторов и операторов присваивания. Однако им все же можно присваивать значения соответствующих встроенных типов, и они поддерживают неявные преобразования в соответствующие встроенные типы. Кроме того, в них определены функции-члены load()
, store()
, exchange()
, compare_exchange_weak()
и compare_exchange_strong()
. Поддерживаются также составные операторы присваивания (там, где это имеет смысл) +=
, -=
, *=
, |=
и т.д., а для целочисленных типов и специализаций std::atomic<>
для указателей — еще и операторы ++
и --
. Этим операторам соответствуют также именованные функции-члены с идентичной функциональностью: fetch_add()
, fetch_or()
и т.д. Операторы присваивания возвращают сохраненное значение, а именованные функции-члены — значение, которое объект имел до начала операции. Это позволяет избежать потенциальных проблем, связанных с тем, что обычно операторы присваивания возвращают ссылку на объект в левой части. Чтобы получить из такой ссылки сохраненное значение, программа должна была бы выполнить еще одну операцию чтения, но тогда между присваиванием и чтением другой поток мог бы модифицировать значение, открывая дорогу гонке.
Но шаблон класса std::atomic<>
— не просто набор специализаций. В нем есть основной шаблон, который можно использовать для создания атомарного варианта пользовательского типа. Поскольку это обобщенный шаблон класса, определены только операции load()
, store()
(а также присваивание значения пользовательского типа и преобразования в пользовательский тип), exchange()
, compare_exchange_weak()
и compare_exchange_strong()
.
У любой операции над атомарными типами имеется необязательный аргумент, задающий требования к семантике упорядочения доступа к памяти. Точный смысл различных вариантов упорядочения обсуждается в разделе 5.3. Пока же достаточно знать, что операции разбиты на три категории.
• Операции сохранения , для которых можно задавать упорядочение memory_order_relaxed
, memory_order_release
и memory_оrder_sеq_cst
.
• Операции загрузки , для которых можно задавать упорядочение memory_order_relaxed
, memory_order_consume
, memory_order_acquire
и memory_order_seq_cst
.
• Операции чтения-модификации-записи , для которых можно задавать упорядочение memory_order_relaxed
, memory_order_consume
, memory_order_acquire
, memory_order_release
, memory_order_acq_rel
и memory_order_seq_cst
.
По умолчанию для всех операций подразумевается упорядочение memory_оrder_sеq_cst
.
Теперь рассмотрим, какие операции можно производить над каждым из стандартных атомарных типов, начиная с std::atomic_flag
.
5.2.2. Операции над std::atomic_flag
Читать дальше