【翻译】InterlockedIncrement内部是如何实现的?

    Interlocked系列函数能够对内存进行原子操做,它是如何实现的?
    它的实现依赖于底层的CPU架构。对于某些CPU来讲,这很简单,例如x86能够经过 LOCK前缀直接支持Interlocked操做(有一个额外的特性就是XCHG指令老是隐式包含了LOCK前缀)。IA64和x64也直接支持原子的load-modify-store操做。
    其它的多数CPU架构把这个操做分红两部分,被称为 Load-link/store-conditional。第一部分(load-link)从指定内存地址读取一个值,而且处理器会监视这个内存地址,看是否有其它处理器修改该值。第二部分(store-conditional)是若是这期间没有其它处理器修改该值,则将新值存回该地址。所以,一个原子的load-link/store-conditional操做就是经过load-link读取值,进行一些计算,而后试图store-conditional。若是store-conditional失败,那么从新开始整个操做。
 1 LONG InterlockedIncrement( LONG volatile *value )
 2 {
 3     LONG        lOriginal, lNewValue;
 4     do
 5     {
 6         //
 7         //经过load-link读取当前值
 8         //能够知道写回以前是否有人修改它
 9         //
10         lOriginal = load_link(value);
11  
12         //
13         //计算新的值
14         //
15         lNewValue = lOriginal + 1;
16  
17         //
18         //有条件的写回新值
19         //若是有人在计算期间覆写该值,则函数返回失败
20         //
21     } while ( !store_conditional(value, lNewValue));
22     return lNewValue;
23 }
(若是看起来有些熟悉,是的,你以前见到过这种模式。)
    请求CPU监视一个内存地址依赖于CPU本身的实现。但要记住一件事情,CPU在同一时间只能监视一个内存地址,而且这个时间是很短暂的。若是你的代码被抢占了或者在load-link后有一个硬件中断到来,那么你的store-conditional将会失败,由于CPU由于硬件中断而分心了,彻底忘记了你要求它监视的内存地址(即便CPU成功的记住了它,也不会记过久,由于硬件中断几乎都会执行本身的load-link指令,所以会替换成它本身要求监视的内存地址)。
    另外,CPU可能会有点懒,在监视时并不监视内存地址,而是监视cache line,若是有人修改了一个不一样的内存位置,可是恰好跟要被监视的内存地址在同一个cache line里,store-conditional操做也会失败,即便它事实上能够成功完成。ARM架构的CPU是太懒了,以致于任何向同一块2048字节写入的操做都会致使store-conditional失败。
    这对于须要用汇编语言来实现Interlocked操做的你来讲意味着什么?你须要尽量减小load-link和store-conditional之间的指令数。例如,InterlockedIncrement只不过是给值加1。你在load-link和store-conditional之间插入的指令越多,store-conditional失败的可能就越大,你就不得不重来一次。若是你在二者之间插入的指令太多了就会致使store-conditional永远不会成功。举一个极端的例子,若是你计算新值的代码须要耗时5秒,在这5秒内确定会接收到不少硬件中断,store-conditional操做就永远都会失败。
 
本文译自The Old New Thing,原文地址http://blogs.msdn.com/b/oldnewthing/archive/2013/09/13/10448736.aspx
相关文章
相关标签/搜索