Ответ 1
Каждый экземпляр и полная специализация std:: atomic < > представляют тип, что разные потоки могут одновременно работать, не поднимая undefined поведение:
Объекты атомных типов являются единственными объектами С++, свободными от гонок данных; то есть, если один поток записывает на атомный объект, а другой поток читает его, поведение корректно определено.
Кроме того, доступ к атомным объектам может устанавливать межпоточную синхронизацию и упорядочивать неатомные обращения к памяти, как указано в
std::memory_order
.
std::atomic<>
обертывает операции, которые в pre-С++ 11 раз выполнялись с использованием (например) взаимосвязанных функций с MSVC или атомные bultins в случае GCC.
Кроме того, std::atomic<>
дает вам больше контроля, позволяя различные порядки памяти, которые определяют ограничения синхронизации и упорядочения. Если вы хотите больше узнать о модели атома и модели памяти С++ 11, эти ссылки могут быть полезными:
- C++ атомизация и упорядочение памяти
- Сравнение: нефиксированное программирование с атоматикой в С++ 11 против мьютекса и блокировки RW
- С++ 11 представил стандартизованную модель памяти. Что это значит? И как это повлияет на программирование на С++?
- Concurrency в С++ 11
Обратите внимание, что для типичных случаев использования вы, вероятно, используете перегруженные арифметические операторы или другой набор из них:
std::atomic<long> value(0);
value++; //This is an atomic op
value += 5; //And so is this
Эти операции будут выполняться с std::memory_order_seq_cst
, так как это порядок по умолчанию для всех атомных операций в С++ 11. Он гарантирует последовательную согласованность (общее глобальное упорядочение) между всеми атомными операциями.
Однако в некоторых случаях это может не потребоваться (и ничего не приходит бесплатно), поэтому вы можете использовать более явную форму:
std::atomic<long> value(0);
value.fetch_add(1, std::memory_order_relaxed); //Atomic, but there are no synchronization or ordering constraints
value.fetch_add(5, std::memory_order_release); //Atomic, performs 'release' operation; guarantees, that no memory accesses in the current thread can be reordered after this store
Теперь, ваш пример:
a = a + 12;
не будет оценивать один атомный op: это приведет к a.load()
(который является атомом), затем добавление между этим значением и 12
и a.store()
(также атомарным) конечного результата.
Однако, если вы напишете a += 12
, это будет атомная операция (как я уже отмечал ранее).
Что касается вашего комментария:
Регулярный
int
имеет атомные нагрузки и запасы. Какова точка его обертывания с помощьюatomic<>
Ваше утверждение верно только для x86. Существуют и другие архитектуры, которые не обеспечивают такую гарантию. std::atomic<>
- это что-то, что гарантировано будет атомарным на каждой платформе.