int i =1 是原子操作吗?i++是原子操作吗?

int i =1 是原子操作吗?i++是原子操作吗?

1、什么是原子操作

原子(atomic)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被中断的一个或一系列操作”。

  • 所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch(切换到另一个线程)。
  • 也可以这样理解:如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构,那么这个操作是一个原子(atomic)操作。
  • 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分。将整个操作视作一个整体是原子性的核心特征。

对于单处理器单核系统来说,只要保证操作指令序列不被打断即可实现原子操作(当然,对于内存的读写操作,需要地址对齐,否则就不是一次的内存读写了,当然也就不是原子操作)。

  • 对于简单的原子操作,cpu实现上会提供单条指令,比如INC和XCHG。
  • 对于复杂的原子操作,需要包含多条指令。执行过程中,出现上下文切换行为,比如任务切换,中断处理等。这里的行为会影响原子操作的原子性。因此需要自旋锁spinlock[1]来保证操作指令序列不会在执行的中途受干扰。

但是如果对于多处理器或者多核的系统,原子操作的实现除了需要spinlock来保证外,还需要保证不会受到同处理器上其他核,或者其他处理器的影响。当其他核上执行的指令访问的内存空间,与当前原子操作需要访问的内存空间存在冲突时,就会破坏原子操作的正确性。

  • 在多核系统中,单个的机器指令就不是原子操作,因为多核系统里是多指令流并行运行的,一个核在执行一个指令时,其他核同时执行的指令有可能操作同一块内存区域,从而出现数据竞争现象。
  • 多核系统中的原子操作通常使用内存栅障(memory
    barrier)来实现,即一个CPU核在执行原子操作时,其他CPU核必须停止对内存操作或者不对指定的内存进行操作,这样才能避免数据竞争问题。

2、示例:凡有赋值的操作在多线程环境都要加锁

1、给整型变量赋值一个确定的值

int a = 1;

这种指令操作一般是原子的。因为对应着一条计算机指令,cpu将立即数1搬运到变量a的内存地址中即可:

int _tmain(int argc, _TCHAR* argv[])
{
	//example 1:
	int a = 1;
	//a=1 汇编指令
	_asm
	{
		mov dword ptr[a], 1
	}
	printf("%d\n", a);
 
	return 0;
}

2、变量自身增加或者减去一个值

i++

.i++分为三个阶段:在cpu执行时

  • 第一步,先将 count所在内存的值加载到寄存器;
  • 第二步,将寄存器的值自增1;
  • 第三步,将寄存器中的值写回内存。
int _tmain(int argc, _TCHAR* argv[])
{
	//example 2:
	int a = 0;
	//a++ 汇编指令
	_asm
	{
		mov eax, dword ptr[a]	//step 1
		inc eax			//step 2
		mov dword ptr[a], eax	//step 3
	}
	printf("%d\n", a);
 
	return 0;
}

现在假设a的值是0,有两个线程,每个线程对变量a的值递增1,我们预想的结果应该是2,可实际运行的结果可能是1!分析如下:

//线程1
void thread_func1(){
	a++;
}
 
//线程2
void thread_func2(){
	a++;
}
  • 我们预想的结果是线程1和线程2的三条指令各自执行,最终a的值为2。
  • 但是由于操作系统线程调度的不确定性,线程1执行完指令step1、2后,eax寄存器中的值为1。
  • 此时操作系统切换到线程2执行,执行指令step1、2、3,此时eax的值变为1;
  • 接着操作系统切回线程1继续执行,执行指令step3,得到a的最终结果1。

3、其他示例
把一个变量的值赋值给另外一个变量,或者把一个表达式的值赋值给另外一个变量,如

int a = b;
  • 从C/C++语法的级别来看,这是也是一条语句,是原子的;
  • 但是从实际执行的二进制指令来看,由于现代计算机CPU架构体系的限制,数据不可以直接从内存搬运到另外一块内存,必须借助寄存器中断,
  • 这条语句一般对应两条计算机指令,即将变量b的值搬运到某个寄存器(如eax)中,再从该寄存器搬运到变量a的内存地址:
mov eax, dword ptr [b]  
mov dword ptr [a], eax 

既然是两条指令,那么多个线程在执行这两条指令时,某个线程可能会在第一条指令执行完毕后被剥夺CPU时间片,切换到另外一个线程而产生不确定的情况。

参考

1、https://blog.youkuaiyun.com/hellokandy/article/details/87912086
2、https://www.jianshu.com/p/a47b141452ce

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值