C++ 使用std::atomic fetch_add的结果索引数组是否仍然是原子的?

C++ 使用std::atomic fetch_add的结果索引数组是否仍然是原子的?,c++,multithreading,atomicity,C++,Multithreading,Atomicity,所以我想用fetch\u add和fetch\u sub原子指令实现一个固定大小、无等待的堆栈。假设我有一个基本堆栈,有两个操作,push和pop struct WaitFreeStack { std::atomic<size_t> cur; std::atomic<int>* buf; void push(int i) { buf[cur.fetch_add(1)].store(i); } int pop()

所以我想用
fetch\u add
fetch\u sub
原子指令实现一个固定大小、无等待的堆栈。假设我有一个基本堆栈,有两个操作,push和pop

struct WaitFreeStack {
    std::atomic<size_t> cur;
    std::atomic<int>* buf;

    void push(int i) {
        buf[cur.fetch_add(1)].store(i);
    }

    int pop() {
        return buf[cur.fetch_sub(1) - 1].load();
    }
};
struct WaitFreeStack{
原子电流;
std::原子*buf;
无效推送(int i){
buf[当前取数\添加(1)]存储(i);
}
int-pop(){
返回buf[cur.fetch_sub(1)-1].load();
}
};
我的问题是,操作是否以
B[X]
的形式进行,其中B是数组,X是整数原子?例如,在我的示例中,在执行
push()
方法的
fetch\u add
调用之后,在执行
B[X]
之前,是否可能在单独的线程中执行整个
pop
push
,从而导致
push
覆盖另一个
push

运算的形式是B[X],其中B是数组,X是整数原子

没有

例如,在我的示例中,在执行push()方法的fetch_add调用之后,在执行B[X]之前,是否可能执行整个pop和push-In单独的线程,从而导致push覆盖另一个push


你的例子可以等同于:

void push(int i) {
    size_t index = cur.fetch_add(1);
    // execution time interval
    buf[index].store(i);
}

int pop() {
    size_t index = cur.fetch_sub(1) - 1;
    // execution time interval
    return buf[index].load();
}
在上面的两个注释位置都会有一个执行时间间隔,尽管时间间隔非常短,但是如果另一个线程调用
push
pop
,并在此时完成调用,则这将是绝对不安全的



实现线程安全容器的最简单方法是使用
std::mutex
,还有一些无锁实现(如)。

B[X]
不是原子的。但即使它是原子的,也不会有多大帮助

问题是您有多个原子操作的表达式。虽然操作是原子的,但整个表达式不是原子的

或者:包含多个原子操作的表达式不需要是原子的

这里的类不变量应该是
cur
指向
buf
中的当前对象。 但是这个不变量在两个原子操作
fetch\u add
store
之间被破坏

如果
B[X]
将是原子的(而不是原子的),则推送将具有以下原子操作序列:

X = cur.fetch_add(1);  // atomic
// 1. dT
ref = B[X];             // let's assume atomic
// 2. dT
ref.store(i);           // atomic
例如,在时间间隔2.dT中,想象第二个线程弹出2个项目,第三个线程推送1个项目,所有这些都在
ref.store(i)
之前执行。此时
ref
reference下的值将发生变化