原子变量自增大致有两种方式;一种是基于CPU硬件支持,而另一种为软件层面自旋实现;两者各有各的优势;理论上说软件
层面的现实是最通用的,不过它也带来了一些棘手的问题比如每个线程都在高频率的执行原子自增那么则会出现原子操作死锁,一
般它主要出现于基于原子信号的TTASLock自旋锁上面;正常情况下没有这么极端所有线程都因原子自增始修正而死循环;但这的确
是软件层面实现存在的一种缺陷。
下面先看原子自增函数的软件层面
public static int Increment(ref int value)
{
int lOrignal, lNewValue;
do
{
lOrignal = value;
lNewValue = lOrignal + 1;
value = lNewValue;
} while (lNewValue != value);
return lNewValue;
}
上述代码若短时间内线程不需要很大量的持续使用时,那么是不会造成死循环的,那么造成死循环的原因在于lNewValue始终
无法等于value由于其它的线程在读value中写入新的值到value 而它们在后置条件的测试中无法通过,过而造成的死循环;解决的办
法是在一段周期还是不可就放弃写入返回ld_link当前值;另外必须要提的一点是你需要显示的告诉语言编译器与CLR当前操作的变量
是可变的,要求编译器与CLR放弃对它的存储器读写优化,减少原子读写碰撞。
下面为基于CPU硬件支持的实现
int __fastcall InterlockedIncrement(volatile int* location)
{
_asm
{
mov ecx, dword ptr[location]
mov eax, 1
lock xadd dword ptr[ecx], eax
inc eax
}
}
上述代码解决了软件层面实现的固有缺陷,但它却需要借助于CPU支持,从现实情况下它兼容从80486 CPU及以上的处理器都
支持它所使用的指令;所以现时代的PC不会存在不兼容的问题。
LOCK指令前缀,它用于标识后方的指令限定的操作存储区域,它可能是寄存器、内存;进行锁定;加以保护;其工作原理与一
般的加锁差异不大;唯一不同之处在于LOCK不保护临界代码,它只保护一次Instruction执行时对存储器的操作;
XADD指令与一般的ADD指令是不同的,它相对的有些特殊,一般向上类似的ADD运算在执行时会把右的寄存器的值与左地址
标识符、内的值相加后存储到左地址标识符内,但不会影响到右值存储器的值,而XADD与ADD功能是几乎相同的,但它会把与寄存
器相加的之前的左地址标识符内的值存放到相加的寄存器内。
--------------------------------------------------
ADD ---- mem/reg | reg/immNOTE: IMM立即数;REG寄存器
XADD---- mem/reg | reg/imm
堆栈演化: