【读书笔记】关于++i是否线程安全的分析?

(之前春招的笔试题中,看到过很多次++i、原子操作的题,这篇博客就做一个分析。)

什么是原子操作?即单指令的操作,单指令操作是不可打断的。

再回顾i++和++i:

i++和++i都是i=i+1的意思,但是过程有些许区别:

i++:先赋值再自加。(例如:i=1;a=1+i++;结果为a=1+1=2,语句执行完后i再进行自加为2)

++i:先自加再赋值。(例如:i=1;a=1+++i;结果为a=1+(1+1)=3,i先自加为2再进行运算)

但是在单独使用时没有区别:如for(int i=0;i<10;i++){ }和for(int i=0;i<10;++i) { }没有区别。
 

再说线程安全:

++i和i++都不是线程安全的,都不是原子操作,因为

1.i++分为三个阶段:

内存到寄存器
寄存器自增
写回内存
这三个阶段中间都可以被中断分离开.

2.++i首先要看编译器是怎么编译的,

某些编译器比如VC在非优化版本中会编译为以下汇编代码:

__asm
{
        moveax,  dword ptr[i]
        inc eax
        mov dwordptr[i], eax
}
这种情况下,必定不是原子操作,不加锁互斥是不行的。
假设加了优化参数,那么是否一定会编译为“inc dword ptr[i]”呢?答案是否定的,这要看编译器心情,如果++i的结果还要被使用的话,那么一定不会被编译为“inc dword ptr[i]”的形式。
那么假设如果编译成了“inc dword ptr[i]”,这是原子操作,是否就不需要加锁了呢?如果在单核机器上,不加锁不会有问题,但到了多核机器上,这个不加锁同样会带来严重后果,两个CPU可以同时执行inc指令,但是两个执行以后,却可能出现只自加了一次。
真正可以确保不“额外”加锁的汇编指令是“lock inc dword ptr[i]”,lock前缀可以暂时锁住总线,这时候其他CPU是无法访问相应数据的。但是目前没有任何一个编译器会将++int编译为这种形式。

 

再看一张汇编代码分析:
 

【读书笔记】关于++i是否线程安全的分析?

 

关于i++和++i的汇编指令区别,可参考以下博客:https://blog.csdn.net/bjbz_cxy/article/details/80226054