c++ 原子操作

原子操作

原子操作是被保证以单独一个事务被执行的操作,其他线程可以看到执行前的系统状态、或者看到原子操作执行结束后的系统状态,但是看不到原子执行中的系统执行状态;

由于总线资源的独占性,一次读或者一次写是天然的原子操作;其他情况下CPU可以通过锁总线和锁cache开始先原子操作

CAS是compare and set,是一条cpu原子指令,一个主要用途是用CAS实现自旋锁

 class Spinlock{
   int i=0;
   void lock(){
       while(!cas(&i,0,1)); // 相当于i++
   }
     
   void release(){
       while(!cas(&i,1,0)); // 相当于i--
   }
 };
bool compare_and_swap(&old_value,expect,new_value);

C++的原子操作

C++11标准模板库中提供了atomic的模板开定义原子类型

  • load() 读操作
  • store()写操作
  • compare_exchange_weak/compare_exchange_strong CAS操作

对于整形还提供一些特殊的成员函数

  • fetch_add() 原子加
  • fetch_sub() 原子减
  • fetch_and() 原子与
  • fetch_or() 原子或

std::atomic_flag,最简单的atomic类型,该对象可以在两个状态之间切换,设置和清除;对象必须被ATOMIC_FLAG_INIT初始化;std::atomic_flag flag = ATOMIC_FLAG_INIT;初始化的对象只能做三件事:销毁,清除、设置;

对应成员函数:

  • clear()
  • test_and_set()

非常适合用在自旋锁中

class spinlock_mutex
 {
   std::atomic_flag flag;
 public:
   spinlock_mutex():
     flag(ATOMIC_FLAG_INIT)
   {}
   void lock()
   {
     while(flag.test_and_set(std::memory_order_acquire));
   }
   void unlock()
   {
     flag.clear(std::memory_order_release);
   }
 };

atomic<>并不能保证是无锁的,会根据硬件设备来选择是无锁实现还是有锁实现:

  1. atomoc_flag可以保证在任何机器硬件上都是无锁的;

  2. atomic中,T的大小是1、2、4、8个字节时,基本可以保证是无锁实现的

  3. 对于自建类型,若是TriviallyCopyable类型,且是”位可比的“ 才能通过无锁实现;

    riviallyCopyable类型等价于memcpy(),自建类型的拷贝、赋值函数是编译器自动生产的,保证CAS操作正常

对于atomic类型,STL提供了id_lock_free(),判断在目前机器中该类型是无锁还是有锁的;

原子类型的CAS函数

  • compare_exchange_weak(expect, desired);
  • compare_exchange_strong(expect, desired);

c++原子操作的六种内存顺序

现在CPU采用过发射技术和流水线技术,为了避免流水线中断,CPU会对指令进行适当的重排

atomic原子操作可以使用memory_order来控制变量在不同线程的可见顺序的可见性,有以下六种内存顺序可选择

 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_release/memory_order_acquire

    memory_order_release是写操作store函数,表示该操作之前的任何写操作都不能放到该操作之后,即写语句不能调到本语句之后;

    memory_order_acquire是读操作load函数,表示该读操作之后的的任何读内存操作都不放到该操作之前

    对于同一个原子变量,release操作之前的写操作,一定对随后的acquire操作后的读可见,这两种内存序一般需要配对使用

  • memory_order_release/memory_order_consume

    此搭配同上一个基本一样,带式该组合是一种更为宽松的内存序可见情况,comsume只是阻止对该院系变量有依赖的操作重排到前面去,而非所有读操作

  • memory_order_acq_rel

    此内存序是release和acquire的结合,包含这两种的特性,因此任何读写操作的重排都不能跨越这个条用

  • memory_order_seq_cst

    默认内存序选项,是最严格的内存顺序,前面的语句不能挑到后面,后面的语句不能调到前面

  • memory_order_relaxed

    只保证当前语句是原子操作,对你内存顺序不做任何保证

C++多线程编程:原子类型与内存顺序 - 知乎 (zhihu.com)
专属学习链接:https://xxetb.xetslk.com/s/36yiy3

  • 28
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值