一直以来,都对原子操作和并发安全都有些误解,认为一个操作不是原子的,那它就不是并发安全的,这样以来,就迟疑了,这么说来:
C语言一个简单的赋值语句也不是原子的咯? a = b是否是原子操作? 见代码如下:
int main() {
int a = 0;
int b = 2;
a = b;
}
编译这段代码: g++ -c -g -Wa,-adlhn Assign.c > Assign.asm
可以发现(只显示a = b部分asm码):
movl -8(%ebp), %eax
movl %eax, -4(%ebp)
大家看,它由两条汇编指令完成操作,简单解释一下:
- a ==> -4(%ebp)
- b ==> -8(%ebp)
- movl与mov参数顺序相反,movl SRC, DEST
- movl -8(%ebp), %eax ==> movl b, %eax (先将寄存器%eax赋值为b的值)
- movl %eax, -4(%ebp) ==> movl %eax, a (将a的值赋值为寄存器%eax的值)
经过这两个指令,最终实现了a = b的赋值
所以,似乎我可以负责任的说,a = b不是原子的。
好吧,既然它不是原子的,那它又是否是并发安全的呢?当然,上面这个例子似乎与并发没多大关系,那就定义这样一个类吧:
class CObject {
private:
int _v;
public:
CObject() { _v = 0; }
void putValue(int v) { _v = v; }
int getValue() const { return _v; }
};
同样是只看_v = v部分的汇编指令
movl 8(%ebp), %eax
movl 12(%ebp), %edx
movl %edx, (%eax)
解释如下:
- 8(%ebp) ==> _v
- 12(%ebp) ==> v
- movl 8(%ebp), %eax ==> %eax赋值为_v的地址
- movl 12(%ebp), %edx ==> %edx赋值为v的地址
- movl %edx, (%eax) ==> %eax对应地址的值赋值为%edx对应的值
过程其实都一样,无非为了说明,赋值操作不是原子的。
如果是我以前的理解,不是原子操作就不是并发安全的,那并发程序该怎么写阿?
其实只能说,这是我的误解,首先我们要仔细想想,在什么情况下才会产生并发问题:
就我的理解,多个线程竞争共享数据时,才会有并发问题,回过头再来看上面那几行汇编代码,_v属于共享数据,可真正产生竞争只可能在movl %edx, (%eax),这个时候,可能有多个线程想进行这一步操作,事实上这个操作应该算是原子的吧,因此这样的赋值操作就应该是并发安全的。