28

我完全欣赏 Threading.Interlocked 类提供的原子性;不过,我不明白为什么 Add 函数只提供两个重载:一个用于 Integer,另一个用于 Long。为什么不使用 Doubles 或任何其他数字类型?

显然,更改 Double 的预期方法是 CompareExchange;我猜这是因为修改 Double 是比修改 Integer 更复杂的操作。我仍然不清楚为什么,如果 CompareExchange 和 Add 都可以接受 Integers,那么它们也不能同时接受 Doubles。

4

4 回答 4

41

其他人已经解决了“为什么?”。Add(ref double, double)然而,使用原语很容易滚动你自己的CompareExchange

public static double Add(ref double location1, double value)
{
    double newCurrentValue = location1; // non-volatile read, so may be stale
    while (true)
    {
        double currentValue = newCurrentValue;
        double newValue = currentValue + value;
        newCurrentValue = Interlocked.CompareExchange(ref location1, newValue, currentValue);
        if (newCurrentValue == currentValue)
            return newValue;
    }
}

CompareExchange如果当前值等于 ,则将 的值设置location1为。由于它以原子的、线程安全的方式执行,我们可以单独依赖它而无需求助于锁。newValuecurrentValue

为什么是while (true)循环?在实现乐观并发算法时,这样的循环是标准的。如果当前值与 不同,CompareExchange则不会改变。我初始化为- 进行非易失性读取(这可能是陈旧的,但这不会改变正确性,因为将检查值)。如果当前值(仍然)是我们从 中读取的值,则将值更改为。如果没有,我们必须使用新的当前值重试,如.location1currentValuecurrentValuelocation1CompareExchangelocationCompareExchangenewValueCompareExchangeCompareExchange

If the value is changed by another thread until the time of our next CompareExchange again, it will fail again, necessitating another retry - and this can in theory go on forever, hence the loop. Unless you are constantly changing the value from multiple threads, CompareExchange will most likely be called only once, if the current value is still what the non-volatile read of location1 yielded, or twice, if it was different.

于 2013-06-03T09:27:35.013 回答
29

Interlocked 类包装了 Windows API Interlocked** 函数。

反过来,它们使用 x86 的 LOCK 指令前缀包装本机处理器 API。它仅支持为以下指令添加前缀:

BT、BTS、BTR、BTC、XCHG、XADD、ADD、OR、ADC、SBB、AND、SUB、XOR、NOT、NEG、INC、DEC

您会注意到,这些反过来又几乎映射到互锁方法。不幸的是,这里不支持非整数类型的 ADD 函数。64 位平台支持添加 64 位长。

这是一篇很棒的文章,讨论了指令级别的锁语义

于 2009-09-09T15:58:23.757 回答
7

正如 Reed Copsey 所说,联锁操作映射(通过 Windows API 函数)到 x86/x64 处理器直接支持的指令。鉴于其中一个功能是 XCHG,您可以执行原子 XCHG 操作,而无需真正关心目标位置的位代表什么。换句话说,代码可以“假装”您正在交换的 64 位浮点数实际上是一个 64 位整数,而 XCHG 指令不会知道其中的区别。因此,.Net 可以通过“假装”它们分别是整数和长整数来为浮点数和双精度数提供 Interlocked.Exchange 函数。

但是,所有其他操作实际上确实对目标的各个位进行了操作,因此除非这些值实际上表示整数(或在某些情况下表示位数组),否则它们将不起作用。

于 2011-06-27T23:37:00.687 回答
1

我怀疑有两个原因。

  1. .Net 所针对的处理器仅支持整数类型的互锁增量。我相信这是 x86 上的 LOCK 前缀,其他处理器可能存在类似的指令。
  2. 如果浮点数足够大,向浮点数加一会产生相同的数字,所以我不确定您是否可以将其称为增量。在这种情况下,也许框架设计者正试图避免不直观的行为。
于 2009-09-09T15:52:02.127 回答