C++ 11在递增原子变量并将其赋值给其他值时,它是原子操作吗?

kak*_*ace 6 c++ multithreading mutex atomic c++11

我对c ++ 11上的原子操作感到困惑,

我知道原子变量自增量是原子操作,

但我使用赋值给其他值,只是怀疑它.

代码就像:

//....
static std::atomic<int> i; // global variable
//....
// in the thread
int id = ++i; 
Run Code Online (Sandbox Code Playgroud)

在不同线程使用赋值时,id是唯一值吗?

测试代码:

#include <thread>
#include <mutex>
#include <atomic>
#include <iostream>

class A {
public:
    static int idGenerator;
    static std::mutex m;
    A () {
        // i know this operation will keep the id_ is unique
        std::lock_guard<std::mutex> lock(m);
        id_ = ++idGenerator; 
    }
   void F(std::string name) {
         std::cout << name << " " << id_ << std::endl;
    }
private:
    int id_;
};
int A::idGenerator = 0;
std::mutex A::m;

class B {
public:
    static int idGenerator;
    B () {
        // after self increment, is the assignment atomic? 
        id_ = (++idGenerator);
    }
   void F(std::string name) {
         std::cout << name << " " << id_.load() << std::endl;
    }
private:
    std::atomic<int> id_;
};
int B::idGenerator = 0;


void funcA() {
    A a2;
    a2.F("a2");
}

void funcB() {
    B b2;
    b2.F("b2");
}

int main() {
    A a1;
    B b1;
    std::thread t1(&funcA);
    std::thread t2(&funcB);
    a1.F("a1");
    b1.F("b1");

    t1.join();
    t2.join();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

有三个线程,

一个类使用lock_guard保持唯一.

B类只使用原子操作,并赋值给变量

Ton*_*roy 9

原子增量函数的规范提供了对其行为的重要洞察 - 来自 http://en.cppreference.com/w/cpp/atomic/atomic/operator_arith的积分T类型:

T operator++();
T operator++() volatile;
T operator++( int );
T operator++( int ) volatile;
Run Code Online (Sandbox Code Playgroud)

注意它们返回一个Tby值,并且永远不会T&从pre-increment 返回通常值.出于这个原因,后递增值的"读取"不是第二个不同的操作,并且是原子增量操作本身的一部分.

另请参见上述链接页面上的"返回值"和"注释"文本.


yoh*_*hjp 5

static std::atomic<int> i; // global variable
// in the thread
int id = ++i; 
Run Code Online (Sandbox Code Playgroud)

在不同线程中使用赋值时,id 是唯一值吗?

是的。C++ 原子变量确保++i将被原子地评估,因此id不同线程上的每个值都是唯一的。

该表达式按id = ++i;以下步骤执行。

  1. 以原子方式递增i,并且 sub-expression( ++i) 在递增后的值上求值。
  2. 将“评估值”分配给id. (这一步是非原子的)


Dev*_*sai 0

卡卡_ace,

不幸的是,在您提供的情况下,它不是原子的。

这里就是为什么预自增操作是原子的,看一下生成的程序集:

 add %l0,1,%l0
Run Code Online (Sandbox Code Playgroud)

(根据所使用的组件可能会略有不同)

但仅此而已。1 操作。这就是为什么它是原子的。

当您将预增量分配给局部变量时,至少有两条指令:

add %l0,1,%l0
st l0, [%fp-4]
Run Code Online (Sandbox Code Playgroud)

这会生成至少两条指令,因此不再是原子的。

请让我知道,如果你有任何问题!