std::atomic

60 阅读2分钟

背景:

在复杂场景中,比如共享数据结构链表,map,等需要多步操作,需要用到互斥锁来保护,而对于简单场景,比如只是一条简单的赋值,加减操作,则加锁解锁显得笨重,也影响性能。因此,c++11引入 std::atomic工具。

基本:

是一个类模板,

1.基本数据类型
std::atomic<char> ac('H');
std::atomic<bool> ai(0);

2.指针类型
std::atomic<char*> ap1(nullptr);
std::atomic<void*> ap2(ullptr);

3.自定义类型
struct Demo {
    Demo();
    int a;
    int b;
};
std::atomic<Demo> ao;

对于自定义类型,类中不能包含以下成员,否则无法作为std::atomic模板参数。
a.拷贝构造函数,拷贝赋值,移动构造,移动赋值,西构函数
b.虚函数
c.包含引用成员

必须支持平凡拷贝(即可以通过memcpy正确拷贝)(简单拷贝)

操作方法:

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

1.读取值 
int v = ai.load(); //注意ai.load()是原子操作,不会被打断,但是赋值到v是非原子性的

2.设置值
ai.store(10);

ai = 10;    

std::cout << "v=" << ai << "\n";//注意:这里atomic并没有重载<<运算符,。这里是将ai转换了int,在cout输出    
    
3.读取旧值,并设置新值,返回旧值
    
int old_v = ai.exchange(20);  
    
4.不同类型支持原子操作不同

a.对于int 类型,
ai += 10; 等价于 ai.fetch_add(10);
ai &= 10; 等价于 ai.fetch_and(10;
ai++;

b.对于指针类型
std::atomic<int*> ap(nullptr);
ap++;
ap+=2;
ap.fetch_add(2)

c.对于double类型的原子变量,不支持上述操作

...

    

CAS操作:

Compare-And-Swap,多线程编程里的原子操作,用来实现无锁同步.

原子的执行:

如果 原子变量的当前值== 预期值(expected)
    则把原子变量更新为新值(desired),并返回true
否则
    把原子变量当前值写回到 expected,返回false

API:


bool compare_exchange_strong(T& expected, T desired);

bool compare_exchange_weak(T& expected,T desired);

strongweak逻辑一样,但区别在于:weak可能因为硬件或优化原因返回false,出现虚假失败

memory_order:内存顺序

  • memory_order_relaxed:当前线程里,编译器和cpu可以随便重排这条原子操作的前后指令
  • memory_order_release:保证这条原子下之前的所有普通读写操作,都不能重排到它后面
  • memory_order_acquire:保证这条原子读之后的所有普通读写操作,都不能被重排到它前面
  • memory_order_acq_rel:既阻止前面的操作被重排到后面,也阻止后面的操作被重排到前面