2014-09-03 387 views
6

我感到困惑的C++ 11的原子操作,当增加原子变量并将其赋值给其他值时,C++ 11是原子操作吗?

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

,但我用的是分配给其他值,只是怀疑。

代码就像:

//.... 
static std::atomic<int> i; // global variable 
//.... 
// in the thread 
int id = ++i; 
使用在不同的线程分配的情况下

,是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; 
} 

有三个线程,

类使用lock_guard保持独特。

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

+0

参见API'的std ::原子:: fetch_add'覆盖在原子单元的两个操作。 – 2014-09-03 04:40:31

+0

对原子变量(或增量操作)的赋值是很原子的。对任何其他变量的赋值不保证是原子的。但是,为了使其“线程安全”,在并发访问的所有场景中,单独使用原子性是不够的。此外,在'B'类看你的代码,看起来你想让静态成员变量idGenerator原子,而不是成员变量'id_'。 – CouchDeveloper 2014-09-03 05:46:59

+0

我读了crtmpserver代码,并且每个连接只有一个iohandler类,iohandler的id由静态的generateId生成。 crtmpserver是Single Process,如果我尝试添加多线程支持,那么id应该保持与前面一样 – 2014-09-03 08:37:17

回答

0

kaka_ace,

不幸的是,你所提供的情况下,它是不是原子。

这里的原因预先递增操作是原子,看生成的汇编:

add %l0,1,%l0 

(可能会有所不同取决于所使用的组件一点点)

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

当你分配一个预增量的局部变量,这至少两个指令:

add %l0,1,%l0 
st l0, [%fp-4] 

,用于产生至少两个指令,因此不再原子。

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

+1

'add%l0,1,%l0'显示代码使用CPU寄存器来存储原子值 - 这不会是无论如何,从其他线程修改,所以是有效的原子。有意思/有意义的情况是,原子变量可能被另一个线程修改的地方 - 然后它是修改内存中原子值*的代码,这是相关的 - C++标准是否需要一个操作码来管理以原子方式增加内存地址并避免第二次读取以获得问题代码中“id”的值。 – 2014-09-03 04:44:44

+0

@TonyD,啊我看到了;谢谢你的澄清和你的回答!喜欢学习新东西,看到并理解线程间共享内存中读取原子性的重要性和上下文。 – 2014-09-03 16:04:39

+0

不用担心 - 很高兴看到一些Sparc大会!实际上 - 我上面的评论谈到需要一个操作码,但这是误导性的,因为在没有这种支持的CPU上,原子操作可能会倒退在互斥体上并涉及许多机器操作码。干杯。 – 2014-09-03 16:28:20

7

的单位递增函数的规范给出了至关重要的洞察他们的行为 - 积分T类型从http://en.cppreference.com/w/cpp/atomic/atomic/operator_arith

T operator++(); 
T operator++() volatile; 
T operator++(int); 
T operator++(int) volatile; 

通知他们返回T的价值,从来没有从预返回通常T& -增量。出于这个原因,增量后值的“读”不是第二个不同的操作,并且是原子增量操作本身的一部分。

另请参阅上述链接页面上的“返回值”和“注释”文本。在使用

+1

谢谢!!!,我使用visual studio 2013来调试代码,它调用atomic_fetch_add来保持原子。 – 2014-09-03 08:25:34

+0

很有参考价值,谢谢! – 2014-09-03 16:05:02

2
static std::atomic<int> i; // global variable 
// in the thread 
int id = ++i; 

在不同的线程分配,是ID独特的价值?

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

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

  1. 原子增量i和子表达式(++i)在增量值后被评估。
  2. 将“评估值”分配给id。 (该步骤是非原子)