Пространства имён
Варианты
Действия

std::atomic

Материал из cppreference.com
 
 
Библиотека атомарных операций
 
 
<tbody> </tbody>
Определено в заголовочном файле <atomic>
template< class T > struct atomic;
(1) (начиная с C++11)
template< class U > struct atomic<U*>;
(2) (начиная с C++11)
Определено в заголовочном файле <memory>
template< class U > struct atomic<std::shared_ptr<U>>;
(3) (начиная с C++20)
template< class U > struct atomic<std::weak_ptr<U>>;
(4) (начиная с C++20)
Определено в заголовочном файле <stdatomic.h>
#define _Atomic(T) /* смотрите ниже */
(5) (начиная с C++23)

Каждое инстанцирование и полная специализация шаблона std::atomic определяет атомарный тип. Если один поток записывает в атомарный объект, а другой поток читает из него, поведение чётко определено (подробности о гонках данных смотрите в модели памяти.

Кроме того, доступ к атомарным объектам может установить межпотоковую синхронизацию и упорядочить доступ к неатомарной памяти, как указано в std::memory_order.

std::atomic нельзя ни копировать, ни перемещать.

Макрос совместимости _Atomic предоставляется в <stdatomic.h>, так что _Atomic(T) идентичен std::atomic<T> пока оба правильно сформированы.

Не указано, доступно ли какое-либо объявление в пространстве имён std, когда включен <stdatomic.h>.

(начиная с C++23)

Специализации

Основной шаблон

Основной шаблон std::atomic может быть создан с любым TriviallyCopyable типом T, соответствующим и CopyConstructible и CopyAssignable. Программа некорректна, если любое из следующих значений false:

  • std::is_trivially_copyable<T>::value
  • std::is_copy_constructible<T>::value
  • std::is_move_constructible<T>::value
  • std::is_copy_assignable<T>::value
  • std::is_move_assignable<T>::value
struct Counters { int a; int b; }; // определённый пользователем тип,
                                   // допускающий тривиальное копирование
std::atomic<Counters> cnt;         // специализация для определённого пользователем типа

std::atomic<bool> использует основной шаблон. Гарантируется, что это будет структура со стандартным выравниванием.

Частичные специализации

Стандартная библиотека предоставляет частичную специализацию шаблона std::atomic для следующих типов с дополнительными свойствами, которых нет у основного шаблона:

2) Частичные специализации std::atomic<U*> для всех типов указателей. Эти специализации имеют стандартное выравнивание, тривиальные конструкторы по умолчанию, (до C++20) и тривиальные деструкторы. Помимо операций, предусмотренных для всех атомарных типов, эти специализации дополнительно поддерживают атомарные арифметические операции, соответствующие типам указателей, такие как fetch_add, fetch_sub.
3,4) Частичные специализации std::atomic<std::shared_ptr<U>> и std::atomic<std::weak_ptr<U>> предусмотрены для std::shared_ptr и std::weak_ptr.

Для подробной информации смотрите std::atomic<std::shared_ptr> и std::atomic<std::weak_ptr>.

(начиная с C++20)

Специализации для целочисленных типов

При создании экземпляра с одним из следующих целых типов std::atomic предоставляет дополнительные атомарные операции, соответствующие целочисленным типам, такие как fetch_add, fetch_sub, fetch_and, fetch_or, fetch_xor:

  • Символьные типы char, char8_t (начиная с C++20), char16_t, char32_t и wchar_t;
  • Стандартные целочисленные типы со знаком: signed char, short, int, long и long long;
  • Стандартные целочисленные типы без знака: unsigned char, unsigned short, unsigned int, unsigned long и unsigned long long;
  • Любые дополнительные целочисленные типы, необходимые для typedef‘ов в заголовке <cstdint>.

Кроме того, получившаяся специализация std::atomic<Целочисленный тип> имеет стандартное выравнивание, тривиальный конструктор по умолчанию, (до C++20) и тривиальный деструктор. Знаковая целочисленная арифметика использует дополнение до двух; нет неопределённых результатов.

Специализации для типов с плавающей запятой

При создании экземпляра с одним из cv-неквалифицированных типов с плавающей запятой (float, double и long double и cv-неквалифицированные расширенные типы с плавающей запятой (начиная с C++23)), std::atomic предоставляет дополнительные атомарные операции, подходящие для типов с плавающей запятой, такие как fetch_add и fetch_sub.

Кроме того, получившаяся специализация std::atomic<Тип с плавающей запятой> имеет стандартное выравнивание и тривиальный деструктор.

Никакие операции не приводят к неопределённому поведению, даже если результат не представляется в виде типа с плавающей запятой. Фактически среда с плавающей запятой может отличаться от среды с плавающей запятой вызывающего потока.

(начиная с C++20)

Псевдонимы типов

Для bool и всех перечисленных выше целочисленных типов предусмотрены следующие псевдонимы типов:

Псевдонимы для всех std::atomic<Целочисленный тип>
std::atomic<bool>
(определение типа) [править]
std::atomic<char>
(определение типа) [править]
std::atomic<signed char>
(определение типа) [править]
std::atomic<unsigned char>
(определение типа) [править]
std::atomic<short>
(определение типа) [править]
std::atomic<unsigned short>
(определение типа) [править]
std::atomic<int>
(определение типа) [править]
std::atomic<unsigned int>
(определение типа) [править]
std::atomic<long>
(определение типа) [править]
std::atomic<unsigned long>
(определение типа) [править]
std::atomic<long long>
(определение типа) [править]
std::atomic<unsigned long long>
(определение типа) [править]
std::atomic<char8_t>
(определение типа) [править]
std::atomic<char16_t>
(определение типа) [править]
std::atomic<char32_t>
(определение типа) [править]
std::atomic<wchar_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::int8_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::uint8_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::int16_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::uint16_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::int32_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::uint32_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::int64_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::uint64_t>
(определение типа) [править]
std::atomic<std::int_least8_t>
(определение типа) [править]
std::atomic<std::uint_least8_t>
(определение типа) [править]
std::atomic<std::int_least16_t>
(определение типа) [править]
std::atomic<std::uint_least16_t>
(определение типа) [править]
std::atomic<std::int_least32_t>
(определение типа) [править]
std::atomic<std::uint_least32_t>
(определение типа) [править]
std::atomic<std::int_least64_t>
(определение типа) [править]
std::atomic<std::uint_least64_t>
(определение типа) [править]
std::atomic<std::int_fast8_t>
(определение типа) [править]
std::atomic<std::uint_fast8_t>
(определение типа) [править]
std::atomic<std::int_fast16_t>
(определение типа) [править]
std::atomic<std::uint_fast16_t>
(определение типа) [править]
std::atomic<std::int_fast32_t>
(определение типа) [править]
std::atomic<std::uint_fast32_t>
(определение типа) [править]
std::atomic<std::int_fast64_t>
(определение типа) [править]
std::atomic<std::uint_fast64_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::intptr_t>
(определение типа) [править]
(C++11)(необязательно)
std::atomic<std::uintptr_t>
(определение типа) [править]
std::atomic<std::size_t>
(определение типа) [править]
std::atomic<std::ptrdiff_t>
(определение типа) [править]
std::atomic<std::intmax_t>
(определение типа) [править]
std::atomic<std::uintmax_t>
(определение типа) [править]
Псевдонимы для специальных типов
знаковый целочисленный атомарный тип, который свободен от блокировок и для которого ожидание/уведомление является наиболее эффективным
(определение типа) [править]
беззнаковый целочисленный атомарный тип, который свободен от блокировок и для которого ожидание/уведомление является наиболее эффективным
(определение типа) [править]

Примечание: std::atomic_intN_t, std::atomic_uintN_t, std::atomic_intptr_t и std::atomic_uintptr_t определены тогда и только тогда, когда std::intN_t, std::uintN_t, std::intptr_t и std::uintptr_t определены соответственно.

std::atomic_signed_lock_free и std::atomic_unsigned_lock_free не обязательны в автономных реализациях.

(начиная с C++20)

Типы-элементы

Тип элемент Определение
value_type T (независимо от того, специализирован или нет)
difference_type value_type (только для специализаций atomic<Целочисленный тип> и atomic<Тип с плавающей запятой> (начиная с C++20))
std::ptrdiff_t (только для специализаций std::atomic<U*>)

difference_type не определён в основном шаблоне std::atomic или в частичных специализациях для std::shared_ptr и std::weak_ptr.

Функции-элементы

создаёт атомарный объект
(public функция-элемент) [править]
сохраняет значение в атомарном объекте
(public функция-элемент) [править]
проверяет, свободен ли атомарный объект от блокировки
(public функция-элемент) [править]
(C++11)
атомарно заменяет значение атомарного объекта неатомарным аргументом
(public функция-элемент) [править]
атомарно получает значение атомарного объекта
(public функция-элемент) [править]
загружает значение из атомарного объекта
(public функция-элемент) [править]
атомарно заменяет значение атомарного объекта и получает ранее сохранённое значение
(public функция-элемент) [править]
атомарно сравнивает значение атомарного объекта с неатомарным аргументом и выполняет атомарный обмен, если они равны, или атомарную загрузку, если нет
(public функция-элемент) [править]
блокирует поток до тех пор, пока не получит уведомление и не изменится атомарное значение
(public функция-элемент) [править]
уведомляет хотя бы один поток, ожидающий атомарный объект
(public функция-элемент) [править]
уведомляет все потоки, заблокированные в ожидании атомарного объекта
(public функция-элемент) [править]

Константы

[static] (C++17)
указывает, что тип всегда свободен от блокировок
(public static константа-элемент) [править]

Специализированные функции-элементы

атомарно добавляет аргумент к значению, хранящемуся в атомарном объекте, и получает значение, сохранённое ранее
(public функция-элемент) [править]
атомарно вычитает аргумент из значения, хранящегося в атомарном объекте, и получает значение, сохранённое ранее
(public функция-элемент) [править]
атомарно выполняет побитовое И между аргументом и значением атомарного объекта и получает значение, сохранённое ранее
(public функция-элемент) [править]
атомарно выполняет побитовое ИЛИ между аргументом и значением атомарного объекта и получает значение, сохранённое ранее
(public функция-элемент) [править]
атомарно выполняет побитовое исключающее ИЛИ между аргументом и значением атомарного объекта и получает значение, сохранённое ранее
(public функция-элемент) [править]
увеличивает или уменьшает атомарное значение на единицу
(public функция-элемент) [править]
добавляет, вычитает или выполняет побитовое И, ИЛИ, исключающее ИЛИ с атомарным значением
(public функция-элемент) [править]

Примечание

Существуют эквиваленты шаблонов функций, не являющихся элементами, для всех функций-элементов std::atomic. Эти функции, не являющиеся элементами, могут быть дополнительно перегружены для типов, которые не являются специализациями std::atomic, но могут гарантировать атомарность. Единственный такой тип в стандартной библиотеке это std::shared_ptr<U>.

_Atomic это ключевое слово и используется для предоставления атомарных типов в C.

Реализации рекомендуются для обеспечения того, чтобы представление _Atomic(T) в C было таким же, как представление std::atomic<T> в C++ для каждого возможного типа T. Механизмы, используемые для обеспечения атомарности и упорядочивания памяти, должны быть совместимы.

В gcc и clang некоторые функции, описанные здесь, требуют связывания с -latomic.

Пример

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

std::atomic_int acnt;
int cnt;

void f()
{
    for(int n = 0; n < 10000; ++n)
    {
        ++acnt;
        ++cnt;
        // Примечание: для этого примера достаточно ослабленно упопорядоченной памяти,
        // например, acnt.fetch_add(1, std::memory_order_relaxed);
    }
}

int main()
{
  {
    std::vector<std::jthread> pool;
    for(int n = 0; n < 10; ++n)
        pool.emplace_back(f);
  }

  std::cout << "Атомарный счётчик равен " << acnt << '\n'
            << "Неатомарный счётчик равен " << cnt << '\n';
}

Возможный вывод:

Атомарный счётчик равен 100000
Неатомарный счётчик равен 69696

Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
LWG 2441 c++11 typedef'фы для атомарных версий необязательных
целочисленных типов фиксированной ширины отсутствовали
добавлены
LWG 3012 c++11 std::atomic<T> был разрешён для
любых T, которые можно копировать только тривиально
такие специализации запрещены
WG не указан c++11 вывод аргументов шаблона для некоторых функций
для атомарных типов может случайно вызвать ошибку;
были предоставлены недопустимые операции с указателями
спецификация была существенно переписана:
добавлены определения типов элементов
value_type и difference_type

Смотрите также

свободный от блокировок логический атомарный тип
(класс) [править]
атомарный разделяемый указатель
(специализация шаблона класса) [править]
атомарный слабый указатель
(специализация шаблона класса) [править]
Документация C по Атомарные типы