欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++ 原子类型

程序员文章站 2024-03-14 11:06:22
...

原子对象可以保证:从不同的线程访问其包含的数据不会造成数据竞争。此外,它还能够同步不同线程对内存的访问。

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 .

C++ 原子类型

当 y 读到的值为 true 时,即说明发生了上述情况。此时两点同步,y.store 之前的内存访问在 y.load 之前之前发生,也就在 y.load 之后的内存访问之前发生。即,read_y_then_x 读到的 x 就是 write_x_then_y 写入的。