atomic - 原子操作

目录

WHAT

ABSTRACT

Template parameters

T

Member Functions

General atomic operations

Operations supported by certain specializations (integral and/or pointer)

HOW

std::atomic::store & load

prototype

abstract

parameters

std::atomic::exchange

prototype

abstract

return value

demo

std::atomic::compare_exchange_weak

prototype

abstract

return value

demo

ASSISTANCE

TEMPLATE

Reference counting - 引用计数

abstract

Implementation

Usage

Spinlock - 自旋锁

abstract

Implementation

Usage

Wait-free ring buffer - 无锁环形队列

abstract

Implementation

Usage

Lock-free multi-producer queue- 无锁多生产者队列

abstract

Implementation

Usage


WHAT

ABSTRACT

Objects of atomic types contain a value of a particular type (T).

The main characteristic of atomic objects is that access to this contained value from different threads cannot cause data races (i.e., doing that is well-defined behavior, with accesses properly sequenced). Generally, for all other objects, the possibility of causing a data race for accessing the same object concurrently qualifies the operation as undefined behavior.

Additionally,
atomic objects have the ability to synchronize access to other non-atomic objects in their threads by specifying different memory orders.

原子类型的对象包含一个特定类型(T)的值。

原子对象的主要特征是,从不同的线程访问这个包含的值不会导致数据竞争(也就是说,这样做是定义良好的行为,访问顺序正确)。一般来说,对于所有其他对象,由于并发访问同一对象而导致数据竞争的可能性,因此将该操作限定为未定义的行为。

此外,通过指定不同的内存顺序,原子对象能够同步访问其线程中的其他非原子对象。

Template parameters

T

Type of the contained value.

This shall be a trivially copyable type.

包含值的类型。 这应该是一个可简单复制的类型。


Member Functions

General atomic operations

(constructor)

Construct atomic (public member function )

构造函数

operator=

Assign contained value (public member function )

传递包含的值(公共成员函数)

is_lock_free

Is lock-free (public member function )

判断在 *this 的基本操作是否存在任意的锁。(公共成员函数)

store

Modify contained value (public member function )

设置*this 的值(公共成员函数)

load

Read contained value (public member function )

获取*this 的值(公共成员函数)

operator T

Access contained value (public member function )

读取并返回该存储的值(公共成员函数)

exchange

Access and modify contained value (public member function )

访问和修改所包含的值(公共成员函数)

compare_exchange_weak

Compare and exchange contained value (weak) (public member function )

比较并改变包含的值(公共成员函数)

比较原子对象所包含值的内容与预期值:

-如果为真,它会用val替换包含的值(像store一样)。

-如果为false,则用包含的值替换expected。

这个函数可能在满足真的情况下仍然返回false,所以可以在循环里使用

compare_exchange_strong

Compare and exchange contained value (strong) (public member function )

比较并改变包含的值(公共成员函数)

比较原子对象所包含值的内容与预期值:

-如果为真,它会用val替换包含的值(像store一样)。

-如果为false,则用包含的值替换expected。

Operations supported by certain specializations (integral and/or pointer)

Do not use floating point types here

fetch_add

Add to contained value (public member function )

fetch_sub

Subtract from contained value (public member function )

fetch_and

Apply bitwise AND to contained value (public member function )

fetch_or

Apply bitwise OR to contained value (public member function )

fetch_xor

Apply bitwise XOR to contained value (public member function )

operator++

Increment container value (public member function )

operator--

Decrement container value (public member function )

HOW

std::atomic::store & load

prototype

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;

abstract

Replaces the contained value with val.

The operation is atomic and follows the memory ordering specified by sync.

parameters

val

Value to copy to the contained object. T is atomic's template parameter (the type of the contained value).

sync

Synchronization mode for the operation. This shall be one of these possible values of the enum type memory_order:

There are six types of memory_orders defined by the C++ standard library, of which memory_order_acq_rel can be regarded as a combination of memory_order_acquire and memory_order_release.

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;

demo

// atomic::load/store example
#include <iostream>       // std::cout
#include <atomic>         // std::atomic, std::memory_order_relaxed
#include <thread>         // std::thread

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

void set_foo(int x) {
  foo.store(x,std::memory_order_relaxed);     // set value atomically
}

void print_foo() {
  int x;
  do {
    x = foo.load(std::memory_order_relaxed);  // get value atomically
  } while (x==0);
  std::cout << "foo: " << x << '\n';
}

int main ()
{
  std::thread first (print_foo);
  std::thread second (set_foo,10);
  first.join();
  second.join();
  return 0;
}

std::atomic::exchange

prototype

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;

abstract

Replaces the contained value by val and returns the value it had immediately before.

The entire operation is atomic (an atomic read-modify-write operation): the value is not affected by other threads between the instant its value is read (to be returned) and the moment it is modified by this function.

将包含的值替换为val并返回它之前的值。

整个操作是原子(原子的读-修改-写操作):价值不受其他线程之间的即时影响它的值是读取(返回),现在是修改这个函数。

return value

The contained value before the call. T is atomic's template parameter (the type of the contained value).

demo

// atomic::exchange example
#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;
}

std::atomic::compare_exchange_weak

prototype

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

abstract

Compares the contents of the atomic object's contained value with expected:

  • if true, it replaces the contained value with val (like store).

  • if false, it replaces expected with the contained value .

The function always accesses the contained value to read it, and -if the comparison is true- it then also replaces it. But the entire operation is atomic: the value cannot be modified by other threads between the instant its value is read and the moment it is replaced.

比较原子对象所包含值的内容与预期值:

-如果为真,它会用val替换包含的值(像store一样)。

-如果为false,则用包含的值替换expected。

该函数总是访问所包含的值来读取它,如果比较为真,那么它也会替换它。

这个函数可能在满足真的情况下仍然返回false,所以可以在循环里使用.

return value

true if expected compares equal to the contained value (and does not fail spuriously). false otherwise.

如果预期的值与所包含的值相等(没有因为代码错误而导致失败),则为True。

否则错误。

demo

// atomic::compare_exchange_weak example:
#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* 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;
}

ASSISTANCE

https://cplusplus.com/reference/atomic/atomic/

https://www.boost.org/doc/libs/1_75_0/doc/html/atomic.html#atomic.introduction

TEMPLATE

Reference counting - 引用计数

abstract

The purpose of a reference counter is to count the number of pointers to an object. The object can be destroyed as soon as the reference counter reaches zero.

Implementation

#include <boost/intrusive_ptr.hpp>
#include <boost/atomic.hpp>

class X {
public:
  typedef boost::intrusive_ptr<X> pointer;
  X() : refcount_(0) {}

private:
  mutable boost::atomic<int> refcount_;
  friend void intrusive_ptr_add_ref(const X * x)
  {
    x->refcount_.fetch_add(1, boost::memory_order_relaxed);
  }
  friend void intrusive_ptr_release(const X * x)
  {
    if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1) {
      boost::atomic_thread_fence(boost::memory_order_acquire);
      delete x;
    }
  }
};

Usage

X::pointer x = new X;

Spinlock - 自旋锁

abstract

The purpose of a spin lock is to prevent multiple threads from concurrently accessing a shared data structure. In contrast to a mutex, threads will busy-wait and waste CPU cycles instead of yielding the CPU to another thread. Do not use spinlocks unless you are certain that you understand the consequences.

旋转锁的目的是防止多个线程并发地访问共享数据结构。与互斥锁相反,线程将忙等待并浪费CPU周期,而不是将CPU让给另一个线程。不要使用自旋锁,除非你确定你明白其后果,可能的使用场景是:在现有系统中的锁操作是短时锁的情况下,要求线程强制一定顺序去执行。

Implementation

#include <boost/atomic.hpp>

class spinlock {
private:
  typedef enum {Locked, Unlocked} LockState;
  boost::atomic<LockState> state_;

public:
  spinlock() : state_(Unlocked) {}

  void lock()
  {
    while (state_.exchange(Locked, boost::memory_order_acquire) == Locked) {
      /* busy-wait */
    }
  }
  void unlock()
  {
    state_.store(Unlocked, boost::memory_order_release);
  }
};

Usage

spinlock s;

s.lock();
// access data structure here
s.unlock();

Wait-free ring buffer - 无锁环形队列

abstract

A wait-free ring buffer provides a mechanism for relaying objects from one single "producer" thread to one single "consumer" thread without any locks. The operations on this data structure are "wait-free" which means that each operation finishes within a constant number of steps. This makes this data structure suitable for use in hard real-time systems or for communication with interrupt/signal handlers.

CAS无锁循环队列提供了一种机制,可以在没有任何锁的情况下将对象从一个“生产者”线程中继到一个“消费者”线程。该数据结构上的操作是“无等待”的,这意味着每个操作在固定数量的步骤内完成。这使得该数据结构适用于硬实时系统或与中断/信号处理程序通信。

Implementation

#include <boost/atomic.hpp>

template<typename T, size_t Size>
class ringbuffer {
public:
  ringbuffer() : head_(0), tail_(0) {}

  bool push(const T & value)
  {
    size_t head = head_.load(boost::memory_order_relaxed);
    size_t next_head = next(head);
    if (next_head == tail_.load(boost::memory_order_acquire))
      return false;
    ring_[head] = value;
    head_.store(next_head, boost::memory_order_release);
    return true;
  }
  bool pop(T & value)
  {
    size_t tail = tail_.load(boost::memory_order_relaxed);
    if (tail == head_.load(boost::memory_order_acquire))
      return false;
    value = ring_[tail];
    tail_.store(next(tail), boost::memory_order_release);
    return true;
  }
private:
  size_t next(size_t current)
  {
    return (current + 1) % Size;
  }
  T ring_[Size];
  boost::atomic<size_t> head_, tail_;
};

Usage

ringbuffer<int, 32> r;

// try to insert an element
if (r.push(42)) { /* succeeded */ }
else { /* buffer full */ }

// try to retrieve an element
int value;
if (r.pop(value)) { /* succeeded */ }
else { /* buffer empty */ }

Lock-free multi-producer queue- 无锁多生产者队列

abstract

The purpose of the lock-free multi-producer queue is to allow an arbitrary number of producers to enqueue objects which are retrieved and processed in FIFO order by a single consumer.

无锁多生产者队列的目的是允许任意数量的生产者排队对象,这些对象由单个消费者按照FIFO顺序检索和处理。

Implementation

template<typename T>
class lockfree_queue {
public:
  struct node {
    T data;
    node * next;
  };
  void push(const T &data)
  {
    node * n = new node;
    n->data = data;
    node * stale_head = head_.load(boost::memory_order_relaxed);
    do {
      n->next = stale_head;
    } while (!head_.compare_exchange_weak(stale_head, n, boost::memory_order_release));
  }

  node * pop_all(void)
  {
    T * last = pop_all_reverse(), * first = 0;
    while(last) {
      T * tmp = last;
      last = last->next;
      tmp->next = first;
      first = tmp;
    }
    return first;
  }

  lockfree_queue() : head_(0) {}

  // alternative interface if ordering is of no importance
  node * pop_all_reverse(void)
  {
    return head_.exchange(0, boost::memory_order_consume);
  }
private:
  boost::atomic<node *> head_;
};

Usage

lockfree_queue<int> q;

// insert elements
q.push(42);
q.push(2);

// pop elements
lockfree_queue<int>::node * x = q.pop_all()
while(x) {
  X * tmp = x;
  x = x->next;
  // process tmp->data, probably delete it afterwards
  delete tmp;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ym影子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值