跳转到内容

C++/Atomic

维基教科书,自由的教学读本
< C++

atomicC++標準程式庫中的一個头文件,定义了C++11标准中的一些表示线程并发控制原子操作的类与方法等。主要声明了两个类模板:std::atomic 和 std::atomic_flag,另外还声明了一套C风格的原子类型和与C兼容的原子操作的函数。

在多线程并发执行时,原子操作是线程不会被打断的执行片段。一些程序设计更为注重性能和效率,需要开发lock-free 的算法和数据结构,这就需要更为底层的原子操作与原子类型。原子类型对象的主要特点就是从不同线程并发访问是良性 (well-defined) 行为,不会导致竞争危害。与之相反,不做适当控制就并发访问非原子对象则会导致未定义 (undifined) 行为。

内存序模型

[编辑]

内存序模型是讨论在原子操作附近的读写操作的顺序。

typedef 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;
  • memory_order_relaxed:只保证原子操作的原子性。
  • memory_order_consume:使用这种内存序的读操作在被影响的内存位置上执行consume操作:当前线程依赖于这个当前读取值的读写操作不能重排序到这条读操作之前;写这个原子变量的其他线程的数据依赖变量的写入对当前线程可见。在大多数平台,这只影响编译器的行为。
  • memory_order_acquire:使用这种内存序的读操作在被影响的内存位置上执行acquire操作:当前线程的读写不能被重排序在这条读操作之前。其他线程对这个原子变量的所有写操作对当前线程是可见的。
  • memory_order_release:使用这种内存序的写操作在被影响的内存位置上执行release操作:当前线程的读写不能被重排序在这条读操作之后。当前线程的所有写操作对其他读这个原子变量的线程是可见的。导致这个原子变量是依赖的写操作对于其他consume这个原子变量的线程是可见的。
  • memory_order_acq_rel:使用这种内存序的一个read-modify-write操作既是acquire操作也是release操作。当前线程的读写不能被重排序在这条操作之前以及之后。其他线程对这个原子变量的release对本线程是可见的。本线程的这条操作结果对其他读取该原子变量的线程是可见的。
  • memory_order_seq_cst:使用这种内存序的读操作在被影响的内存位置上执行acquire操作,使用这种内存序的写操作在被影响的内存位置上执行release操作,使用这种内存序的一个read-modify-write操作既是acquire操作也是release操作。并存在一个全序,所有线程看到的所有修改是同样的顺序。

atomic_flag类型

[编辑]

atomic_flag是一种简单的原子布尔类型,只支持两种操作:test_and_set 和 clear。

结合 std::atomic_flag::test_and_set() 和 std::atomic_flag::clear(),std::atomic_flag 对象可以当作一个简单的自旋锁(spin lock)使用。

  • 构造函数。
    • atomic_flag() noexcept = default; 只有默认构造函数
    • atomic_flag (const atomic_flag&T) = delete;禁用拷贝构造函数
    • 移动构造函数实际上也禁用。根据C++标准的一般规定,如果一个类显式声明了拷贝构造函数(即使是作为delete) ,就不会有缺省的移动构造函数。
    • 相关的宏:如果在初始化时没有明确使用宏ATOMIC_FLAG_INIT初始化,那么新创建的 std::atomic_flag 对象的状态是未指定的(unspecified),既没有被 set 也没有被 clear;如果使用该宏初始化,该std::atomic_flag对象在创建时处于 clear 状态。
  • 成员函数
    • bool test_and_set (memory_order sync = memory_order_seq_cst) volatile noexcept; 返回该std::atomic_flag对象当前状态,如果当前状态为false则设置(set)该对象为true。该函数是原子的。
    • bool test_and_set (memory_order sync = memory_order_seq_cst) noexcept;
    • void clear (memory_order sync = memory_order_seq_cst) volatile noexcept;清除 std::atomic_flag 对象的标志位,即设置 atomic_flag 的值为 false。
    • void clear (memory_order sync = memory_order_seq_cst) noexcept;

示例:

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

std::atomic_flag lock = ATOMIC_FLAG_INIT;
std::stringstream stream;

void append_numer(int x)
{
  while (lock.test_and_set());
  stream << "thread#" << x << "\n";
  lock.clear();
}

int main()
{
  std::vector<std::thread> ths;
  for (int i=0; i<10; i++)
    ths.push_back(std::thread(append_numer, i));
  for (int i=0; i<10; i++)
    ths[i].join();
  std::cout << stream.str();
  return 0;
}

std::atomic类模板

[编辑]

std::atomic比std::atomic_flag功能更加完善,因此有更多的成员方法。

template < class T > struct atomic {
    atomic() = default; //缺省构造, std::atomic 对象处于未初始化(uninitialized)状态
    constexpr atomic(T)) noexcept; //普通构造函数,由类型T初始化一个 std::atomic对象
    atomic(const atomic &) = delete; //禁止拷贝构造
    atomic & operator=(const atomic &) = delete; //禁止拷贝赋值
    atomic & operator=(const atomic &) volatile = delete;
    T operator=(T) volatile noexcept; //从类型T的隐式转换赋值
    T operator=(T) noexcept;
    bool is_lock_free() const volatile; //判断该 std::atomic对象是否具备lock-free的特性。如果某个对象满足 lock-free 特性,在多个线程访问该对象时不会导致线程阻塞。
    bool is_lock_free() const;
    void store(T, memory_order = memory_order_seq_cst) volatile;//修改被封装的值,参数 sync 指定内存序
    void store(T, memory_order = memory_order_seq_cst);
    T load(memory_order = memory_order_seq_cst) const volatile;//读取被封装的值,参数 sync 设置内存序
    T load(memory_order = memory_order_seq_cst) const;
    operator  T() const volatile; //与load()的功能类似,也是读取被封装的值
    operator  T() const;
    T exchange(T, memory_order = memory_order_seq_cst) volatile;//读取并修改被封装的值,用val指定的值替换该原子对象封装的值,并返回该原子对象之前封装的值,整个过程是原子的,因此exchange 操作也称为 read-modify-write 操作。
    T exchange(T, memory_order = memory_order_seq_cst);
    bool compare_exchange_weak (T& expected, T val, memory_order sync = memory_order_seq_cst) volatile noexcept;//比较被封装的值(weak)与参数expected所指定的值的物理内容是否相等,如果相等,则用val替换原子对象的旧值;如果不相等,则用原子对象的旧值替换expected。因此调用该函数之后,如果被该原子对象封装的值与参数 expected 所指定的值不相等,expected 中的内容就是原子对象的旧值。整个操作是原子的,当前线程读取和修改该原子对象时,别的线程不能对读取和修改该原子对象。与compare_exchange_strong不同, weak版本的compare-and-exchange操作允许spuriously 返回 false,即原子对象所封装的值与参数 expected 的物理内容相同,但却仍然返回 false,这在使用循环操作的算法下是可以接受的,并且在一些平台下weak版本的性能更好
    bool compare_exchange_weak (T& expected, T val, memory_order sync = memory_order_seq_cst) noexcept;
    bool compare_exchange_weak (T& expected, T val,  memory_order success, memory_order failure) volatile noexcept;//功能同上。内存序(Memory Order)的选择取决于比较操作结果,如果比较结果为 true(即原子对象的值等于 expected),则选择参数 success 指定的内存序,否则选择参数 failure 所指定的内存序。
    bool compare_exchange_weak (T& expected, T val, memory_order success, memory_order failure) noexcept;    
    bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst) volatile; //与weak版本的差别在于,如果原子对象所封装的值与参数 expected 的物理内容相同,比较操作一定会为 true。
    bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst);
    bool compare_exchange_strong(T &, T, memory_order, memory_order) volatile;
    bool compare_exchange_strong(T &, T, memory_order, memory_order);
};

C++11标准库std::atomic提供了针对整形(integral)和指针类型的特化实现。

针对整形(integal)的特化,其中 integal 代表了如下类型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:

template <> struct atomic<integral> {
    bool is_lock_free() const volatile;
    bool is_lock_free() const;
 
    void store(integral, memory_order = memory_order_seq_cst) volatile;
    void store(integral, memory_order = memory_order_seq_cst);
 
    integral load(memory_order = memory_order_seq_cst) const volatile;
    integral load(memory_order = memory_order_seq_cst) const;
 
    operator integral() const volatile;
    operator integral() const;
 
    integral exchange(integral, memory_order = memory_order_seq_cst) volatile;
    integral exchange(integral, memory_order = memory_order_seq_cst);
 
    bool compare_exchange_weak(integral&, integral, memory_order, memory_order) volatile;
    bool compare_exchange_weak(integral&, integral, memory_order, memory_order);
 
    bool compare_exchange_strong(integral&, integral, memory_order, memory_order) volatile;
    bool compare_exchange_strong(integral&, integral, memory_order, memory_order);
 
    bool compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst) volatile;
    bool compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst);
 
    bool compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst) volatile;
    bool compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst);
 
    integral fetch_add(integral, memory_order = memory_order_seq_cst) volatile;//原子对象的封装值加 val,并返回原子对象的旧值。整个过程是原子的
    integral fetch_add(integral, memory_order = memory_order_seq_cst);
 
    integral fetch_sub(integral, memory_order = memory_order_seq_cst) volatile;
    integral fetch_sub(integral, memory_order = memory_order_seq_cst);
 
    integral fetch_and(integral, memory_order = memory_order_seq_cst) volatile;
    integral fetch_and(integral, memory_order = memory_order_seq_cst);
 
    integral fetch_or(integral, memory_order = memory_order_seq_cst) volatile;
    integral fetch_or(integral, memory_order = memory_order_seq_cst);
 
    integral fetch_xor(integral, memory_order = memory_order_seq_cst) volatile;
    integral fetch_xor(integral, memory_order = memory_order_seq_cst);
     
    atomic() = default;
    constexpr atomic(integral);
    atomic(const atomic&) = delete;
 
    atomic& operator=(const atomic&) = delete;
    atomic& operator=(const atomic&) volatile = delete;
     
    integral operator=(integral) volatile;
    integral operator=(integral);
     
    integral operator++(int) volatile; //后缀++
    integral operator++(int);
    integral operator--(int) volatile; /后缀--
    integral operator--(int);
    integral operator++() volatile; //前缀++
    integral operator++();
    integral operator--() volatile; //前缀--
    integral operator--();
    integral operator+=(integral) volatile;
    integral operator+=(integral);
    integral operator-=(integral) volatile;
    integral operator-=(integral);
    integral operator&=(integral) volatile;
    integral operator&=(integral);
    integral operator|=(integral) volatile;
    integral operator|=(integral);
    integral operator^=(integral) volatile;
    integral operator^=(integral);
};

针对指针的特化:

template <class T> struct atomic<T*> {
    bool is_lock_free() const volatile;
    bool is_lock_free() const;
 
    void store(T*, memory_order = memory_order_seq_cst) volatile;
    void store(T*, memory_order = memory_order_seq_cst);
 
    T* load(memory_order = memory_order_seq_cst) const volatile;
    T* load(memory_order = memory_order_seq_cst) const;
 
    operator T*() const volatile;
    operator T*() const;
 
    T* exchange(T*, memory_order = memory_order_seq_cst) volatile;
    T* exchange(T*, memory_order = memory_order_seq_cst);
 
    bool compare_exchange_weak(T*&, T*, memory_order, memory_order) volatile;
    bool compare_exchange_weak(T*&, T*, memory_order, memory_order);
 
    bool compare_exchange_strong(T*&, T*, memory_order, memory_order) volatile;
    bool compare_exchange_strong(T*&, T*, memory_order, memory_order);
 
    bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst) volatile;
    bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst);
 
    bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst) volatile;
    bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst);
 
    T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst) volatile;
    T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst);
 
    T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst) volatile;
    T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst);
 
    atomic() = default;
    constexpr atomic(T*);
    atomic(const atomic&) = delete;
 
    atomic& operator=(const atomic&) = delete;
    atomic& operator=(const atomic&) volatile = delete;
 
    T* operator=(T*) volatile;
    T* operator=(T*);
    T* operator++(int) volatile;
    T* operator++(int);
    T* operator--(int) volatile;
    T* operator--(int);
    T* operator++() volatile;
    T* operator++();
    T* operator--() volatile;
    T* operator--();
    T* operator+=(ptrdiff_t) volatile;
    T* operator+=(ptrdiff_t);
    T* operator-=(ptrdiff_t) volatile;
    T* operator-=(ptrdiff_t);
};

基于std::atomic_flag类的C风格API

[编辑]
  • bool atomic_flag_test_and_set (volatile atomic_flag* obj) noexcept;
  • bool atomic_flag_test_and_set (atomic_flag* obj) noexcept;
  • bool atomic_flag_test_and_set (volatile atomic_flag* obj, memory_order sync) noexcept;
  • bool atomic_flag_test_and_set (atomic_flag* obj, memory_order sync) noexcept;
  • void atomic_flag_clear (volatile atomic_flag* obj) noexcept;
  • void atomic_flag_clear (atomic_flag* obj) noexcept;
  • void atomic_flag_clear (volatile atomic_flag* obj, memory_order sync) noexcept;
  • void atomic_flag_clear (atomic_flag* obj, memory_order sync) noexcept;

基于std::atomic类模板的C风格API

[编辑]
  • template <class T> bool atomic_is_lock_free (const volatile atomic<T>* obj) noexcept;
  • template <class T> bool atomic_is_lock_free (const atomic<T>* obj) noexcept;
  • bool atomic_is_lock_free (const volatile A* obj) noexcept;
  • bool atomic_is_lock_free (const A* obj) noexcept;
  • template <class T> void atomic_init (volatile atomic<T>* obj, T val) noexcept;//初始化原子对象。如果对一个已初始化的原子对象再次调用 atomic_init(),则会导致未定义行为(undefined behavior)
  • template <class T> void atomic_init (atomic<T>* obj, T val) noexcept;
  • void atomic_init (volatile A* obj, T val) noexcept;
  • void atomic_init (A* obj, T val) noexcept;
  • template <class T> void atomic_store (volatile atomic<T>* obj, T val) noexcept;
  • template <class T> void atomic_store (atomic<T>* obj, T val) noexcept;
  • void atomic_store (volatile A* obj, T val) noexcept;
  • void atomic_store (A* obj, T val) noexcept;
  • template <class T> T atomic_load (const volatile atomic<T>* obj) noexcept;
  • template <class T> T atomic_load (const atomic<T>* obj) noexcept;
  • T atomic_load (const volatile A* obj) noexcept;
  • T atomic_load (const A* obj) noexcept;
  • template <class T> T atomic_load_explicit (const volatile atomic<T>* obj, memory_order sync) noexcept;
  • template <class T> T atomic_load_explicit (const atomic<T>* obj, memory_order sync) noexcept;
  • T atomic_load_explicit (const volatile A* obj, memory_order sync) noexcept;
  • T atomic_load_explicit (const A* obj, memory_order sync) noexcept;
  • template <class T> T atomic_exchange (volatile atomic<T>* obj, T val) noexcept;
  • template <class T> T atomic_exchange (atomic<T>* obj, T val) noexcept;
  • T atomic_exchange (volatile A* obj, T val) noexcept;
  • T atomic_exchange (A* obj, T val) noexcept;
  • template <class T> T atomic_store_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept;
  • template <class T> T atomic_store_explicit (atomic<T>* obj, T val, memory_order sync) noexcept;
  • T atomic_store_explicit (volatile A* obj, T val, memory_order sync) noexcept;
  • T atomic_store_explicit (A* obj, T val, memory_order sync) noexcept;
  • template <class T> bool atomic_compare_exchange_weak (volatile atomic<T>* obj, T* expected, T val) noexcept;
  • template <class T> bool atomic_compare_exchange_weak (atomic<T>* obj, T* expected, T val) noexcept;
  • bool atomic_compare_exchange_weak (volatile A* obj, T* expected, T val) noexcept;
  • bool atomic_compare_exchange_weak (A* obj, T* expected, T val) noexcept;
  • template <class T> bool atomic_compare_exchange_weak_explicit (volatile atomic<T>* obj,T* expected, T val, memory_order success, memory_order failure) noexcept;
  • template <class T> bool atomic_compare_exchange_weak_explicit (atomic<T>* obj,T* expected, T val, memory_order success, memory_order failure) noexcept;
  • bool atomic_compare_exchange_weak_explicit (volatile A* obj,T* expected, T val, memory_order success, memory_order failure) noexcept;
  • bool atomic_compare_exchange_weak_explicit (A* obj, T* expected, T val, memory_order success, memory_order failure) noexcept;
  • template <class T> bool atomic_compare_exchange_strong (volatile atomic<T>* obj, T* expected, T val) noexcept;
  • template <class T> bool atomic_compare_exchange_strong (atomic<T>* obj, T* expected, T val) noexcept;
  • bool atomic_compare_exchange_strong (volatile A* obj, T* expected, T val) noexcept;
  • bool atomic_compare_exchange_strong (A* obj, T* expected, T val) noexcept;
  • template <class T> bool atomic_compare_exchange_strong_explicit (volatile atomic<T>* obj, T* expected, T val, memory_order success, memory_order failure) noexcept;
  • template <class T> bool atomic_compare_exchange_strong_explicit (atomic<T>* obj, T* expected, T val, memory_order success, memory_order failure) noexcept;
  • bool atomic_compare_exchange_strong_explicit (volatile A* obj, T* expected, T val, memory_order success, memory_order failure) noexcept;
  • bool atomic_compare_exchange_strong_explicit (A* obj, T* expected, T val, memory_order success, memory_order failure) noexcept;
  • template <class T> T atomic_fetch_add (volatile atomic<T>* obj, T val) noexcept;
  • template <class T> T atomic_fetch_add (atomic<T>* obj, T val) noexcept;
  • template <class U> U* atomic_fetch_add (volatile atomic<U*>* obj, ptrdiff_t val) noexcept;
  • template <class U> U* atomic_fetch_add (atomic<U*>* obj, ptrdiff_t val) noexcept;
  • T atomic_fetch_add (volatile A* obj, M val) noexcept;
  • T atomic_fetch_add (A* obj, M val) noexcept;
  • template <class T> T atomic_fetch_add_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept;
  • template <class T> T atomic_fetch_add_explicit (atomic<T>* obj, T val, memory_order sync) noexcept;
  • template <class U> U* atomic_fetch_add_explicit (volatile atomic<U*>* obj, ptrdiff_t val, memory_order sync) noexcept;
  • template <class U> U* atomic_fetch_add_explicit (atomic<U*>* obj, ptrdiff_t val, memory_order sync) noexcept;
  • T atomic_fetch_add_explicit (volatile A* obj, M val, memory_order sync) noexcept;
  • T atomic_fetch_add_explicit (A* obj, M val, memory_order sync) noexcept;
  • template <class T> T atomic_fetch_sub (volatile atomic<T>* obj, T val) noexcept;
  • template <class T> T atomic_fetch_sub (atomic<T>* obj, T val) noexcept;
  • template <class U> U* atomic_fetch_sub (volatile atomic<U*>* obj, ptrdiff_t val) noexcept;
  • template <class U> U* atomic_fetch_sub (atomic<U*>* obj, ptrdiff_t val) noexcept;
  • T atomic_fetch_sub (volatile A* obj, M val) noexcept;
  • T atomic_fetch_sub (A* obj, M val) noexcept;
  • template <class T> T atomic_fetch_sub_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept;
  • template <class T> T atomic_fetch_sub_explicit (atomic<T>* obj, T val, memory_order sync) noexcept;
  • template <class U> U* atomic_fetch_sub_explicit (volatile atomic<U*>* obj, ptrdiff_t val, memory_order sync) noexcept;
  • template <class U> U* atomic_fetch_sub_explicit (atomic<U*>* obj, ptrdiff_t val, memory_order sync) noexcept;
  • T atomic_fetch_sub_explicit (volatile A* obj, M val, memory_order sync) noexcept;
  • T atomic_fetch_sub_explicit (A* obj, M val, memory_order sync) noexcept;
  • template <class T> T atomic_fetch_and (volatile atomic<T>* obj, T val) noexcept;
  • template <class T> T atomic_fetch_and (atomic<T>* obj, T val) noexcept;
  • T atomic_fetch_and (volatile A* obj, T val) noexcept;
  • T atomic_fetch_and (A* obj, T val) noexcept;
  • template <class T> T atomic_fetch_and_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept;
  • template <class T> T atomic_fetch_and_explicit (atomic<T>* obj, T val, memory_order sync) noexcept;
  • T atomic_fetch_and_explicit (volatile A* obj, T val, memory_order sync) noexcept;
  • T atomic_fetch_and_explicit (A* obj, T val, memory_order sync) noexcept;
  • template <class T> T atomic_fetch_or (volatile atomic<T>* obj, T val) noexcept;
  • template <class T> T atomic_fetch_or (atomic<T>* obj, T val) noexcept;
  • T atomic_fetch_or (volatile A* obj, T val) noexcept;
  • T atomic_fetch_or (A* obj, T val) noexcept;
  • template <class T> T atomic_fetch_or_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept;
  • template <class T> T atomic_fetch_or_explicit (atomic<T>* obj, T val, memory_order sync) noexcept;
  • T atomic_fetch_or_explicit (volatile A* obj, T val, memory_order sync) noexcept;
  • T atomic_fetch_or_explicit (A* obj, T val, memory_order sync) noexcept;
  • template <class T> T atomic_fetch_xor (volatile atomic<T>* obj, T val) noexcept;
  • template <class T> T atomic_fetch_xor (atomic<T>* obj, T val) noexcept;
  • T atomic_fetch_xor (volatile A* obj, T val) noexcept;
  • T atomic_fetch_xor (A* obj, T val) noexcept;
  • template <class T> T atomic_fetch_xor_explicit (volatile atomic<T>* obj, T val, memory_order sync) noexcept;
  • template <class T> T atomic_fetch_xor_explicit (atomic<T>* obj, T val, memory_order sync) noexcept;
  • T atomic_fetch_xor_explicit (volatile A* obj, T val, memory_order sync) noexcept;
  • T atomic_fetch_xor_explicit (A* obj, T val, memory_order sync) noexcept;

与原子对象初始化相关的宏

[编辑]
  • ATOMIC_VAR_INIT(val):初始化 std::atomic 对象。
  • ATOMIC_FLAG_INIT:初始化 std::atomic_flag 对象。

例子程序

[编辑]
#include <iostream>
#include <atomic>
#include <vector>
#include <thread>
#include <sstream>

std::atomic<int> foo(0);

void set_foo(int x)
{
  foo = x;
}

void print_foo()
{
  while (foo == 0)
  {
    std::this_thread::yield();
  }
  std::cout << "x: " << foo << std::endl;
}
int main()
{
  std::thread print_th(print_foo);
  std::thread set_th(set_foo, 10);
  print_th.join();
  set_th.join();
  return 0;
}

参考文献

[编辑]