跳至內容

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;
}

參考文獻

[編輯]