C++ 原子類型

原子對象可以保證:從不同的線程訪問其包含的數據不會造成數據競爭。此外,它還能夠同步不同線程對內存的訪問。

atomic

構造

default (1) atomic() noexcept = default;
initialization (2) constexpr atomic (T val) noexcept;
copy [deleted] (3) atomic (const atomic&) = delete;

賦值

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;

原子操作,使用順序一致性(memory_order_seq_cst)。

訪問

修改包含的值

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;

原子操作。

sync 取值如下:

value memory order description
memory_order_relaxed Relaxed No synchronization of side effects.
memory_order_release Release Synchronizes side effects with the next consume or acquire operation.
memory_order_seq_cst Sequentially consistent Synchronizes all visible side effects with the other sequentially consistent operations, following a single total order.

讀取包含的值

T load (memory_order sync = memory_order_seq_cst) const volatile noexcept;
T load (memory_order sync = memory_order_seq_cst) const noexcept;

原子操作。

sync 取值如下:

value memory order description
memory_order_relaxed Relaxed No synchronization of side effects.
memory_order_consume Consume Synchronizes the visible side effects on values carrying dependencies from the last release or sequentially consistent operation.
memory_order_acquire Acquire Synchronizes all visible side effects from the last release or sequentially consistent operation.
memory_order_seq_cst Sequentially consistent Synchronizes all visible side effects with the other sequentially consistent operations, following a single total order.

交換操作

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;

將包含的值修改爲 val,並返回此前包含的值。

原子操作。

sync 取值如下:

value memory order description
memory_order_relaxed Relaxed No synchronization of side effects.
memory_order_consume Consume Synchronizes the visible side effects on values carrying dependencies from the last release or sequentially consistent operation.
memory_order_acquire Acquire Synchronizes all visible side effects from the last release or sequentially consistent operation.
memory_order_release Release Synchronizes side effects with the next consume or acquire operation.
memory_order_acq_rel Acquire/Release Reads as an acquire operation and writes as a release operation (as described above).
memory_order_seq_cst Sequentially consistent Synchronizes all visible side effects with the other sequentially consistent operations, following a single total order.

類型轉換

operator T() const volatile noexcept;
operator T() const noexcept;

原子操作,使用順序一致性(memory_order_seq_cst

比較並交換

(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 的值與原子對象包含的值進行比較:

  • 如果相等,則將原子對象包含的值替換爲 val ;
  • 如果不相等,則將 expected 的值替換爲原子對象包含的值。

原子操作。

此函數直接比較 expected 和 原子對象所包含的值的物理內容(類似於 memcmp),而不是使用 operator== 進行比較,所以,如果底層類型有 padding bit 等都將導致比較結果不相等。

形式(2)使用的內存序取決於比較結果:如果相等,則使用 success 指定的內存序;否則使用 failure 指定的內存序。

和 compare_exchange_strong 不同,此函數允許 虛假失敗:即使比較結果相等,也可以返回 false 。

返回值:如果比較結果相等,則返回 true(不包括虛假失敗);否則返回 false 。

sync 取值如下:

value memory order description
memory_order_relaxed Relaxed No synchronization of side effects.
memory_order_consume Consume Synchronizes the visible side effects on values carrying dependencies from the last release or sequentially consistent operation.
memory_order_acquire Acquire Synchronizes all visible side effects from the last release or sequentially consistent operation.
memory_order_release Release Synchronizes side effects with the next consume or acquire operation.
memory_order_acq_rel Acquire/Release Reads as an acquire operation and writes as a release operation (as described above).
memory_order_seq_cst Sequentially consistent Synchronizes all visible side effects with the other sequentially consistent operations, following a single total order.
(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;

和 compare_exchange_weak 類似,但是不允許虛假失敗。

// atomic::compare_exchange_weak example:
#include <iostream>
#include <atomic>
#include <thread>
#include <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* oldHead = list_head;
  Node* newNode = new Node {val,oldHead};

  // what follows is equivalent to: list_head = newNode, but in a thread-safe way:
  while (!list_head.compare_exchange_weak(oldHead,newNode))
    newNode->next = oldHead;
}

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;
}
 3 5 4 7 6 8 0 9 2 1

特化操作

if T is integral (1) T fetch_add (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;

T fetch_add (T val, memory_order sync = memory_order_seq_cst) noexcept;
if T is pointer (2) T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept;

T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept;

將原子對象包含的值加上 val,並返回此前原子對象包含的值。

if T is integral (1) T fetch_sub (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;

T fetch_sub (T val, memory_order sync = memory_order_seq_cst) noexcept;
if T is pointer (2) T fetch_sub (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept;

T fetch_sub (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept;

將原子對象包含的值減去 val,並返回此前原子對象包含的值。

:T 爲整數類型

T fetch_and (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;

T fetch_and (T val, memory_order sync = memory_order_seq_cst) noexcept;

將原子對象包含的值替換爲它與 val 相與的結果,並返回此前原子對象包含的值。

:T 爲整數類型

T fetch_or (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_or (T val, memory_order sync = memory_order_seq_cst) noexcept;

將原子對象包含的值替換爲它與 val 相或的結果,並返回此前原子對象包含的值。

異或:T 爲整數類型

T fetch_xor (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;

T fetch_xor (T val, memory_order sync = memory_order_seq_cst) noexcept;

將原子對象包含的值替換爲它與 val 相異或的結果,並返回此前原子對象包含的值。

自增 & 自減:T 爲整數類型或指針類型

pre-increment (1) T operator++() volatile noexcept;

T operator++() noexcept;
post-increment (2) T operator++ (int) volatile noexcept;

T operator++ (int) noexcept;
pre-decrement (1) T operator--() volatile noexcept;

T operator--() noexcept;
post-decrement (2) T operator-- (int) volatile noexcept;

T operator-- (int) noexcept;

運算符

T is integral (1) T operator+= (T val) volatile noexcept;
T operator+= (T val) noexcept;
T operator-= (T val) volatile noexcept;
T operator-= (T val) noexcept;
T operator&= (T val) volatile noexcept;
T operator&= (T val) noexcept;
T operator|= (T val) volatile noexcept;
T operator|= (T val) noexcept;
T operator^= (T val) volatile noexcept;
T operator^= (T val) noexcept;
if T is pointer (2) T operator+= (ptrdiff_t val) volatile noexcept;
T operator+= (ptrdiff_t val) noexcept;
T operator-= (ptrdiff_t val) volatile noexcept;
T operator-= (ptrdiff_t val) noexcept;

返回值都是操作之前原子對象包含的值。

atomic_flag

它是一個布爾類型的原子對象。

構造

atomic_flag() noexcept = default;
atomic_flag (const atomic_flag&T) = delete;

構造之後,atomic_flag 對象需要使用 ATOMIC_FLAG_INIT 初始化,以使其處於 clear 狀態。

操作

test and set

bool test_and_set (memory_order sync = memory_order_seq_cst) volatile noexcept;
bool test_and_set (memory_order sync = memory_order_seq_cst) noexcept;

設置該 atomic_flag 對象,並返回此前此 atomic_flag 對象是否處於 set 狀態。

原子操作。

sync 取值如下:

value memory order description
memory_order_relaxed Relaxed No synchronization of side effects.
memory_order_consume Consume Synchronizes the visible side effects on values carrying dependencies from the last release or sequentially consistent operation.
memory_order_acquire Acquire Synchronizes all visible side effects from the last release or sequentially consistent operation.
memory_order_release Release Synchronizes side effects with the next consume or acquire operation.
memory_order_acq_rel Acquire/Release Reads as an acquire operation and writes as a release operation (as described above).
memory_order_seq_cst Sequentially consistent Synchronizes all visible side effects with the other sequentially consistent operations, following a single total order.

clear

void clear (memory_order sync = memory_order_seq_cst) volatile noexcept;
void clear (memory_order sync = memory_order_seq_cst) noexcept;

讓 atomic_flag 對象處於 clear 狀態。

sync 取值如下:

value memory order description
memory_order_relaxed Relaxed No synchronization of side effects.
memory_order_consume Consume Synchronizes the visible side effects on values carrying dependencies from the last release or sequentially consistent operation.
memory_order_acquire Acquire Synchronizes all visible side effects from the last release or sequentially consistent operation.
memory_order_release Release Synchronizes side effects with the next consume or acquire operation.
memory_order_seq_cst Sequentially consistent Synchronizes all visible side effects with the other sequentially consistent operations, following a single total order.
// using atomic_flag as a lock
#include <iostream>
#include <atomic>
#include <thread>
#include <vector>
#include <sstream>

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

void append_number(int x) {
	// set狀態:鎖定狀態
	// clear狀態:解鎖狀態
	while (lock_stream.test_and_set()) {}
	stream << "thread #" << x << '\n';
	lock_stream.clear();
}

int main() {
	std::vector<std::thread> threads;
	for (int i = 1; i <= 10; ++i) threads.push_back(std::thread(append_number, i));
	for (auto& th : threads) th.join();

	std::cout << stream.str();
	return 0;
}
thread #2
thread #4
thread #1
thread #3
thread #5
thread #6
thread #7
thread #8
thread #9
thread #10

內存序

typedef enum 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
} memory_order;

當多個線程對同一個原子對象執行原子操作時,只有當一個原子操作執行完之後,其他的原子操作才能夠訪問該原子對象。這保證不會在這些原子對象上發生數據競爭。

但是每個線程可能會操作內存位置(如 load 操作從內存加載一個值),並且這些操作可能會給其他線程造成某些可見的副作用。memory_order 允許爲操作指定一個內存序,以決定這些可見副作用如何在線程之間同步,且使用原子操作作爲同步點。

  • memory_order_relaxed

    最寬鬆的內存序。

    操作會發生,但不知道什麼時候發生。

    The operation is ordered to happen atomically at some point.
    This is the loosest memory order, providing no guarantees on how memory accesses in different threads are ordered with respect to the atomic operation.

  • memory_order_consume

    [應用於 loading 操作]

    一旦 releasing 線程中所有依賴於 releasing 操作(對 loading 線程會造成可見副作用)的內存訪問都已經發生了,該操作便會發生。

    The operation is ordered to happen once all accesses to memory in the releasing thread that carry a dependency on the releasing operation (and that have visible side effects on the loading thread) have happened.

  • memory_order_acquire

    [應用於 loading 操作]

    一旦 releasing 線程中所有的內存訪問(對 loading 線程會造成可見的副作用)都已經發生了,該操作便會發生。

    The operation is ordered to happen once all accesses to memory in the releasing thread (that have visible side effects on the loading thread) have happened.

  • memory_order_release

    [應用於 storing 操作]

    該操作在一個 consume 或 acquire 操作之前發生。

    The operation is ordered to happen before a consume or acquire operation, serving as a synchronization point for other accesses to memory that may have visible side effects on the loading thread.

  • memory_order_acq_rel

    [應用於 loading/storing 操作]

    load 操作同 memory_order_acquire,store 操作同 memeory_order_release 。

    The operation loads acquiring and stores releasing (as defined above for memory_order_acquire and memory_order_release).

  • memory_order_seq_cst

    最嚴格的內存序。

    一旦所有可能會對其他線程造成可見副作用的內存訪問都發生了,該操作纔會發生。

    consume 和 acquire 加載操作、順序一致的存儲操作都是 releasing 操作。

    The operation is ordered in a sequentially consistent manner: All operations using this memory order are ordered to happen once all accesses to memory that may have visible side effects on the other threads involved have already happened.
    This is the strictest memory order, guaranteeing the least unexpected side effects between thread interactions though the non-atomic memory accesses.
    For consume and acquire loads, sequentially consistent store operations are considered releasing operations.

如:acquire 和 release

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

bool x;
std::atomic<bool> y;
std::atomic<int> z;

void write_x_then_y() {
	x = true;
	y.store(true, std::memory_order_release);
}

void read_y_then_x() {
	while (!y.load(std::memory_order_consume));
	if (x) {
		++z;
	}
}

int main() {
	int n = 1000;

	for (int i = 0; i < n; i++) {
		x = false;
		y = false;
		z = 0;

        std::thread b(read_y_then_x);
		std::thread a(write_x_then_y);

		a.join();
		b.join();
		
		std::cout << z.load() << std::endl;
	}
}

此處 z 永遠不會爲 0 .

在這裏插入圖片描述

當 y 讀到的值爲 true 時,即說明發生了上述情況。此時兩點同步,y.store 之前的內存訪問在 y.load 之前之前發生,也就在 y.load 之後的內存訪問之前發生。即,read_y_then_x 讀到的 x 就是 write_x_then_y 寫入的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章