为什么没有接受 Doubles 作为参数的 Interlocked.Add 重载?
我完全欣赏 Threading.Interlocked 类提供的原子性;但我不明白为什么 Add 函数只提供两种重载:一种用于整数,另一种用于长整型。为什么不使用双精度数或任何其他数字类型呢?
显然,更改 Double 的预期方法是 CompareExchange;我猜测这是因为修改 Double 是比修改 Integer 更复杂的操作。我仍然不清楚为什么,如果 CompareExchange 和 Add 都可以接受整数,那么它们不能同时接受双精度数。
I fully appreciate the atomicity that the Threading.Interlocked class provides; I don't understand, though, why the Add function only offers two overloads: one for Integers, another for Longs. Why not Doubles, or any other numeric type for that matter?
Clearly, the intended method for changing a Double is CompareExchange; I am GUESSING this is because modifying a Double is a more complex operation than modifying an Integer. Still it isn't clear to me why, if CompareExchange and Add can both accept Integers, they can't also both accept Doubles.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
其他人已经解决了“为什么?”。然而,使用
CompareExchange
原语来创建自己的Add(ref double, double)
是很容易的:CompareExchange
设置的值如果当前值等于
为currentValue
,则 location1newValue
。由于它以原子、线程安全的方式执行此操作,因此我们可以单独依赖它,而无需求助于锁。为什么要使用
while (true)
循环?在实现乐观并发算法时,这样的循环是标准的。如果当前值与currentValue
不同,CompareExchange
不会更改location1
。我将currentValue
初始化为location1
- 进行非易失性读取(这可能是过时的,但这不会改变正确性,因为CompareExchange
会检查值)。如果当前值(仍然)是我们从location1
读取的值,CompareExchange
会将值更改为newValue
。如果不是,我们必须使用CompareExchange
返回的新当前值重试CompareExchange
。如果该值被另一个线程更改,直到再次进行下一个
CompareExchange
时,它将再次失败,需要再次重试 - 理论上这可以永远持续下去,因此会出现循环。除非您不断地从多个线程更改值,否则如果当前值仍然是location1
的非易失性读取产生的值,则CompareExchange
很可能只会被调用一次,或者两次,如果不同的话。更新 2022/8/17
正如 Strangelove 博士和 Theodor Zoulias 在评论中指出的,当
location1 == Double.NaN
时,Add()
将变成无限循环。所以我不得不
改为
Others have addressed the "why?". It is easy however to roll your own
Add(ref double, double)
, using theCompareExchange
primitive:CompareExchange
sets the value oflocation1
to benewValue
, if the current value equalscurrentValue
. As it does so in an atomic, thread-safe way, we can rely on it alone without resorting to locks.Why the
while (true)
loop? Loops like this are standard when implementing optimistically concurrent algorithms.CompareExchange
will not changelocation1
if the current value is different fromcurrentValue
. I initializedcurrentValue
tolocation1
- doing a non-volatile read (which might be stale, but that does not change the correctness, asCompareExchange
will check the value). If the current value (still) is what we had read fromlocation1
,CompareExchange
will change the value tonewValue
. If not, we have to retryCompareExchange
with the new current value, as returned byCompareExchange
.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 oflocation1
yielded, or twice, if it was different.Update 2022/8/17
As Dr. Strangelove and Theodor Zoulias pointed out in the comments, when
location1 == Double.NaN
,Add()
would turn into an infinite loop.So I had to change
to
Interlocked 类包装了 Windows API Interlocked** 函数。
这些反过来又使用 x86 的 LOCK 指令前缀来包装本机处理器 API。它仅支持添加以下指令的前缀:
您会注意到,这些反过来又几乎映射到互锁方法。不幸的是,这里不支持非整数类型的 ADD 函数。 64 位平台支持 64 位长整型的添加。
这是一篇很棒的文章讨论指令级别的锁语义。
The Interlocked class wraps around the Windows API Interlocked** functions.
These are, in turn, wrapping around the native processor API, using the LOCK instruction prefix for x86. It only supports prefixing the following instructions:
You'll note that these, in turn, pretty much map to the interlocked methods. Unfortunately, the ADD functions for non-integer types are not supported here. Add for 64bit longs is supported on 64bit platforms.
Here's a great article discussing lock semantics on the instruction level.
正如 Reed Copsey 所说,互锁操作(通过 Windows API 函数)映射到 x86/x64 处理器直接支持的指令。鉴于这些函数之一是 XCHG,您可以执行原子 XCHG 操作,而无需真正关心目标位置处的位代表什么。换句话说,代码可以“假装”您正在交换的 64 位浮点数实际上是 64 位整数,并且 XCHG 指令不会知道其中的差异。因此,.Net 可以通过“假装”浮点数和双精度数分别是整数和长整数来为它们提供 Interlocked.Exchange 函数。
但是,所有其他操作实际上都对目标的各个位进行操作,因此除非这些值实际上表示整数(或在某些情况下表示位数组),否则它们将不起作用。
As Reed Copsey has said, the Interlocked operations map (via Windows API functions) to instructions supported directly by the x86/x64 processors. Given that one of those functions is XCHG, you can do an atomic XCHG operation without really caring what the bits at the target location represent. In other words, the code can "pretend" that the 64-bit floating point number you are exchanging is in fact a 64-bit integer, and the XCHG instruction won't know the difference. Thus, .Net can provide Interlocked.Exchange functions for floats and doubles by "pretending" that they are integers and long integers, respectively.
However, all of the other operations actually do operate on the individual bits of the destination, and so they won't work unless the values actually represent integers (or bit arrays in some cases.)
我怀疑有两个原因。
I suspect that there are two reasons.