C++原子变量atomic详解

发布时间 2023-09-27 21:49:44作者: 好人~

b站视频
文章1

C++中原子变量确保共享变量的操作在执行时不会被其他线程的操作干扰。
无法复制/移动对象。

is_lock_free函数:atomic对象是否支持无锁操作(什么意思?如果atomic对象需要锁,那设为atomic对象的意义是什么?)

std::atomic_flag 是 C++ 中的一个原子布尔类型,它用于实现原子锁操作。什么叫原子锁?

有没有锁,有什么影响吗?
原子操作也不见得是无锁的,使用is lock free和is always lock free
进行判断。

memory_order

atomic对象可以通过指定不同的memory orders来控制其对其他非原子对象的访问顺序和可见性,从而实现线程安全。常用的memory orders包括(参考:链接):

memory_order_relaxed

memory_order_relaxed: 无序的内存访问,允许编译器做更多的优化。

#include<bits/stdc++.h>
#include<thread>
using namespace std;

atomic<bool> x,y;
atomic<int> z;
void write_x_then_y()
{
    x.store(true, memory_order_relaxed);
    y.store(true, memory_order_relaxed);
}
void read_y_then_x()
{
    while(!y.load(memory_order_relaxed));  // y为true时会退出while
    if (x.load(memory_order_relaxed))    // x也为true时,z的值会改变
        ++z;
}

int main() {
    x=false;
    y=false;
    z=0;
    thread a(write_x_then_y);
    thread b(read_y_then_x);
    a.join();
    b.join();
    cout <<  noboolalpha << (z.load()!=0) << endl;
}

仅保证该原子类型变量的操作是原子化的,write_x_then_y()中对x和y的赋值是无序的,也就是说可能出现先给y赋值然后给x赋值,也可能出现先给x赋值然后给y赋值。所以read_y_then_x()中可能出现y为true,但x不为true的情况。(我的理解)
对上面代码进行实际测试,并没有出现y为true但x不为true的情况。这不能说明问题,因为其他编译器的优化策略,可能会导致出现y为true但x不为true的情况。

memory_order_release与memory_order_acquire

memory_order_release:

#include<bits/stdc++.h>
#include<thread>
using namespace std;

atomic<bool> x,y;
atomic<int> z;
// void write_x_then_y()
// {
//     x.store(true, memory_order_relaxed);
//     y.store(true, memory_order_release);
// }
// 下面函数和上面函数等价
void write_x_then_y()
{
    x.store(true, memory_order_relaxed);
    atomic_thread_fence(memory_order_release);
    y.store(true, memory_order_relaxed);
}

void read_y_then_x()
{
    while(!y.load(memory_order_acquire));  // y为true时会退出while
    if (x.load(memory_order_relaxed))    // x也为true时,z的值会改变
        ++z;
}

int main() {
    x=false;
    y=false;
    z=0;
    thread a(write_x_then_y);
    thread b(read_y_then_x);
    a.join();
    b.join();
    cout <<  noboolalpha << (z.load()!=0) << endl;
}

y.store(true, memory_order_release);保证了此语句前的语句写入完成。y.load(memory_order_acquire)用来“接住”使用memory_order_release写入的y。由于y.store(true, memory_order_release);保证了此语句前的语句写入完成,所以y.load(memory_order_acquire)后面的语句读取到的x都写入完成的x。(我的理解)

memory_order_seq_cst

memory_order_seq_cst是原子操作的默认参数,代码的顺序是怎么样的,底层的内存访问顺序就是怎么样的。(我的理解)

memory_order_consume

编译器一般都不怎么支持memory_order_consume,它保证本次读取之前所有依赖于该原子类型变量值的操作都已完成,但不保证其他线程对该变量的存储结果已经可见。(看不太懂)

自定义类型作为原子类型

atomic:T可以是自定类型,不过有以下注意点:

必须使用编译器生成的复制构造函数?
不可以有虚函数或继承虚类
operator=等价与operator& ?

如下是否是懒汉式?:
Singleton& Singleton::getInstance() {
static Singleton obj;
return obj;
}