C++11 併發指南六(atomic 類型詳解一 atomic_flag 介紹) 一文介紹了 C++11 中最簡單的原子類型 std::atomic_flag,可是 std::atomic_flag 過於簡單,只提供了 test_and_set 和 clear 兩個 API,不能知足其餘需求(如 store, load, exchange, compare_exchange 等),所以本文將介紹功能更加完善的 std::atomic 類。html
std::atomic 是模板類,一個模板類型爲 T 的原子對象中封裝了一個類型爲 T 的值。node
template <class T> struct atomic;
原子類型對象的主要特色就是從不一樣線程訪問不會致使數據競爭(data race)。所以從不一樣線程訪問某個原子對象是良性 (well-defined) 行爲,而一般對於非原子類型而言,併發訪問某個對象(若是不作任何同步操做)會致使未定義 (undifined) 行爲發生。ios
C++11 標準中的基本 std::atomic 模板定義以下:算法
template < class T > struct atomic { 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); 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); };
另外,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:app
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; 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 有了一個最基本認識以後咱們來看 std::atomic 的成員函數吧。性能
std::atomic 的構造函數以下:fetch
default (1) | atomic() noexcept = default; |
---|---|
initialization (2) | constexpr atomic (T val) noexcept; |
copy [deleted] (3) | atomic (const atomic&) = delete; |
請看下例:ui
#include <iostream> // std::cout #include <atomic> // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT #include <thread> // std::thread, std::this_thread::yield #include <vector> // std::vector // 由 false 初始化一個 std::atomic<bool> 類型的原子變量 std::atomic<bool> ready(false); std::atomic_flag winner = ATOMIC_FLAG_INIT; void do_count1m(int id) { while (!ready) { std::this_thread::yield(); } // 等待 ready 變爲 true. for (volatile int i=0; i<1000000; ++i) {} // 計數 if (!winner.test_and_set()) { std::cout << "thread #" << id << " won!\n"; } } int main () { std::vector<std::thread> threads; std::cout << "spawning 10 threads that count to 1 million...\n"; for (int i=1; i<=10; ++i) threads.push_back(std::thread(count1m,i)); ready = true; for (auto& th : threads) th.join(); return 0; }
std::atomic 的賦值操做函數定義以下:
set value (1) | T operator= (T val) noexcept; T operator= (T val) volatile noexcept; |
---|---|
copy [deleted] (2) | atomic& operator= (const atomic&) = delete; atomic& operator= (const atomic&) volatile = delete; |
能夠看出,普通的賦值拷貝操做已經被禁用。可是一個類型爲 T 的變量能夠賦值給相應的原子類型變量(至關與隱式轉換),該操做是原子的,內存序(Memory Order) 默認爲順序一致性(std::memory_order_seq_cst),若是須要指定其餘的內存序,需使用 std::atomic::store()。
#include <iostream> // std::cout #include <atomic> // std::atomic #include <thread> // std::thread, std::this_thread::yield std::atomic <int> foo = 0; void set_foo(int x) { foo = x; // 調用 std::atomic::operator=(). } void print_foo() { while (foo == 0) { // wait while foo == 0 std::this_thread::yield(); } std::cout << "foo: " << foo << '\n'; } int main() { std::thread first(print_foo); std::thread second(set_foo, 10); first.join(); second.join(); return 0; }
本節主要介紹基本 std::atomic 類型所具有的操做(即成員函數)。咱們知道 std::atomic 是模板類,一個模板類型爲 T 的原子對象中封裝了一個類型爲 T 的值。本文<std::atomic 基本介紹>一節中也提到了 std::atomic 類模板除了基本類型之外,還針對整形和指針類型作了特化。 特化的 std::atomic 類型支持更多的操做,如 fetch_add, fetch_sub, fetch_and 等。本小節介紹基本 std::atomic 類型所具有的操做:
bool is_lock_free() const volatile noexcept; bool is_lock_free() const noexcept;
void store (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; void store (T val, memory_order sync = memory_order_seq_cst) noexcept;
Memory Order 值 | Memory Order 類型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_release | Release |
memory_order_seq_cst | Sequentially consistent |
#include <iostream> // std::cout #include <atomic> // std::atomic, std::memory_order_relaxed #include <thread> // std::thread std::atomic<int> foo(0); // 全局的原子對象 foo void set_foo(int x) { foo.store(x, std::memory_order_relaxed); // 設置(store) 原子對象 foo 的值 } void print_foo() { int x; do { x = foo.load(std::memory_order_relaxed); // 讀取(load) 原子對象 foo 的值 } while (x == 0); std::cout << "foo: " << x << '\n'; } int main () { std::thread first(print_foo); // 線程 first 打印 foo 的值 std::thread second(set_foo, 10); // 線程 second 設置 foo 的值 first.join(); second.join(); return 0; }
T load (memory_order sync = memory_order_seq_cst) const volatile noexcept; T load (memory_order sync = memory_order_seq_cst) const noexcept;
Memory Order 值 | Memory Order 類型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_seq_cst | Sequentially consistent |
#include <iostream> // std::cout #include <atomic> // std::atomic, std::memory_order_relaxed #include <thread> // std::thread std::atomic<int> foo(0); // 全局的原子對象 foo void set_foo(int x) { foo.store(x, std::memory_order_relaxed); // 設置(store) 原子對象 foo 的值 } void print_foo() { int x; do { x = foo.load(std::memory_order_relaxed); // 讀取(load) 原子對象 foo 的值 } while (x == 0); std::cout << "foo: " << x << '\n'; } int main () { std::thread first(print_foo); // 線程 first 打印 foo 的值 std::thread second(set_foo, 10); // 線程 second 設置 foo 的值 first.join(); second.join(); return 0; }
operator T() const volatile noexcept; operator T() const noexcept;
#include <iostream> // std::cout #include <atomic> // std::atomic #include <thread> // std::thread, std::this_thread::yield std::atomic<int> foo = 0; std::atomic<int> bar = 0; void set_foo(int x) { foo = x; } void copy_foo_to_bar() { // 若是 foo == 0,則該線程 yield, // 在 foo == 0 時, 實際也是隱含了類型轉換操做, // 所以也包含了 operator T() const 的調用. while (foo == 0) std::this_thread::yield(); // 實際調用了 operator T() const, 將foo 強制轉換成 int 類型, // 而後調用 operator=(). bar = static_cast<int>(foo); } void print_bar() { // 若是 bar == 0,則該線程 yield, // 在 bar == 0 時, 實際也是隱含了類型轉換操做, // 所以也包含了 operator T() const 的調用. while (bar == 0) std::this_thread::yield(); std::cout << "bar: " << bar << '\n'; } int main () { std::thread first(print_bar); std::thread second(set_foo, 10); std::thread third(copy_foo_to_bar); first.join(); second.join(); third.join(); return 0; }
T exchange (T val, memory_order sync = memory_order_seq_cst) volatile noexcept; T exchange (T val, memory_order sync = memory_order_seq_cst) noexcept;
Memory Order 值 | Memory Order 類型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
請看下面例子,各個線程計數至 1M,首先完成計數任務的線程打印本身的 ID,
#include <iostream> // std::cout #include <atomic> // std::atomic #include <thread> // std::thread #include <vector> // std::vector std::atomic<bool> ready(false); std::atomic<bool> winner(false); void count1m (int id) { while (!ready) {} // wait for the ready signal for (int i = 0; i < 1000000; ++i) {} // go!, count to 1 million if (!winner.exchange(true)) { std::cout << "thread #" << id << " won!\n"; } }; int main () { std::vector<std::thread> threads; std::cout << "spawning 10 threads that count to 1 million...\n"; for (int i = 1; i <= 10; ++i) threads.push_back(std::thread(count1m,i)); ready = true; for (auto& th : threads) th.join(); return 0; }
(1) | bool compare_exchange_weak (T& expected, T val, memory_order sync = memory_order_seq_cst) volatile noexcept; bool compare_exchange_weak (T& expected, T val, memory_order sync = memory_order_seq_cst) noexcept; |
---|---|
(2) | bool compare_exchange_weak (T& expected, T val, memory_order success, memory_order failure) volatile noexcept; bool compare_exchange_weak (T& expected, T val, memory_order success, memory_order failure) noexcept; |
expected 的值不會改變。
Memory Order 值 | Memory Order 類型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
#include <iostream> // std::cout #include <atomic> // std::atomic #include <thread> // std::thread #include <vector> // std::vector // a simple global linked list: struct Node { int value; Node* next; }; std::atomic<Node*> list_head(nullptr); void append(int val) { // append an element to the list Node* newNode = new Node{val, list_head}; // next is the same as: list_head = newNode, but in a thread-safe way: while (!list_head.compare_exchange_weak(newNode->next,newNode)) {} // (with newNode->next updated accordingly if some other thread just appended another node) } int main () { // spawn 10 threads to fill the linked list: std::vector<std::thread> threads; for (int i = 0; i < 10; ++i) threads.push_back(std::thread(append, i)); for (auto& th : threads) th.join(); // print contents: for (Node* it = list_head; it!=nullptr; it=it->next) std::cout << ' ' << it->value; std::cout << '\n'; // cleanup: Node* it; while (it=list_head) {list_head=it->next; delete it;} return 0; }
9 8 7 6 5 4 3 2 1 0
(1) | bool compare_exchange_strong (T& expected, T val, memory_order sync = memory_order_seq_cst) volatile noexcept; bool compare_exchange_strong (T& expected, T val, memory_order sync = memory_order_seq_cst) noexcept; |
---|---|
(2) | bool compare_exchange_strong (T& expected, T val, memory_order success, memory_order failure) volatile noexcept; bool compare_exchange_strong (T& expected, T val, memory_order success, memory_order failure) noexcept; |
Memory Order 值 | Memory Order 類型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
#include <iostream> // std::cout #include <atomic> // std::atomic #include <thread> // std::thread #include <vector> // std::vector // a simple global linked list: struct Node { int value; Node* next; }; std::atomic<Node*> list_head(nullptr); void append(int val) { // append an element to the list Node* newNode = new Node{val, list_head}; // next is the same as: list_head = newNode, but in a thread-safe way: while (!(list_head.compare_exchange_strong(newNode->next, newNode))); // (with newNode->next updated accordingly if some other thread just appended another node) } int main () { // spawn 10 threads to fill the linked list: std::vector<std::thread> threads; for (int i = 0; i < 10; ++i) threads.push_back(std::thread(append, i)); for (auto& th : threads) th.join(); // print contents: for (Node* it = list_head; it!=nullptr; it=it->next) std::cout << ' ' << it->value; std::cout << '\n'; // cleanup: Node* it; while (it=list_head) {list_head=it->next; delete it;} return 0; }
好了,本文花了大量的篇幅介紹 std::atomic 基本類型,下一篇博客我會給你們介紹 C++11 的標準庫中std::atomic 針對整形(integral)和指針類型的特化版本作了哪些改進。