我一直在寻找使用Interlocked的线程安全计数器实现,该实现支持按任意值递增,并直接从 Interlocked.CompareExchange 文档中找到了此示例(为简单起见,略有更改):

private int totalValue = 0;

public int AddToTotal(int addend)
{
    int initialValue, computedValue;
    do
    {
        // How can we get away with not using a volatile read of totalValue here?
        // Shouldn't we use CompareExchange(ref TotalValue, 0, 0)
        // or Thread.VolatileRead
        // or declare totalValue to be volatile?
        initialValue = totalValue;

        computedValue = initialValue + addend;

    } while (initialValue != Interlocked.CompareExchange(
        ref totalValue, computedValue, initialValue));

    return computedValue;
}

 public int Total
 {
    // This looks *really* dodgy too, but isn't
    // the target of my question.
    get { return totalValue; }
 }

我知道这段代码正在尝试做的事情,但是我不确定在分配给要添加到的临时变量时不使用 volatile 的共享变量读取方法如何解决。
initialValue是否有可能在整个循环中保持陈旧的值,从而使该函数永不返回?还是CompareExchange中的内存屏障(?)消除了这种可能性?任何见识将不胜感激。

编辑:我应该澄清一下,我理解如果CompareExchange导致上次totalValue调用之后对CompareExchange的后续读取是最新的,那么此代码就可以了。但是可以保证吗?

最佳答案

托管的Interlocked.CompareExchange直接映射到Win32 API中的 InterlockedCompareExchange (还有64 bit version)。

如您在函数签名中所看到的, native API要求目标必须是 volatile 的,即使托管API并不需要该目标,但乔·达菲(Joe Duffy)在其出色的著作Concurrent Programming on Windows中建议使用volatile。

09-06 01:42