首先什么是原子操作?
原子本意是“不能被进一步分割的最小粒子”,而原子操作意为”不可被中断的一个或一系列操作”;
处理器如何实现原子操作?
- 首先处理器会自动保证基本的内存操作的原子性:处理器保证从系统内存当中读取或者写入一个字节是原子的,意思是当一个处理器读取一个字节时,其他处理器不能访问这个字节的内存地址。
- 总线锁保证原子性:所谓总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占使用共享内存。
- 缓存锁定保证原子性:锁住总线的消耗太大,于是有了缓存锁,在Lock期间处理器缓存的内存区域会被锁定,其他处理其无法更改该块内存中的数据
现代的大多数CPU都实现了CAS,它是一种==无锁==(lock-free),且==非阻塞==的一种算法,保持数据的一致性
CAS的思想很简单:三个参数,一个当前内存值V、旧的预期值A、即将更新的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做,并返回false。
cas步骤还原:
- 需要传递三个参数 1.当前线程中获取的旧值 2.新值 3.内存地址中的值
- 循环比较旧值和内存地址中的值,直到比较成功为止,即使失败,旧值是用volatile修饰的,保证一旦发生改变,能够被其他线程所察觉.然后再进行比较.
代码示例
Exchange:
int exchange(std::atomic<int>& i, int j)
{
int old_value(0);
int new_value(0);
do
{
old_value = i;
new_value = j;
}
while (!i.compare_exchange_strong(old_value, new_value));
return new_value;
}
上面的函数是用CAS实现的,没有使用atomic的fetch_and_add,exchange。
那么CAS是怎么保证数据的正确的?注意上面的old_value,old_value中保存着i的副本,在i执行CAS时,如果old_value中的值于i相同,即代表i没有被其他线程修改,保持原值,随后CAS操作成功,i替换为new_value 。如果old_value与i不相等,即代表其他线程修改了i,随后CAS操作失败,重新刷新old_value,new_value 并继续循环。注意这里的重新刷新是为了保证new_value的数值正确,new_value的是基于old_value计算得到,如果old_value与i不相等,即代表new_value的数值是错误的,需要重新刷新然后再去争抢。