C# 为什么不存在接受双精度作为参数的Interlocated.Add重载?
我完全理解Threading.Interlocked类提供的原子性;不过,我不明白为什么Add函数只提供两个重载:一个用于整数,另一个用于long。为什么不使用double或任何其他数字类型呢C# 为什么不存在接受双精度作为参数的Interlocated.Add重载?,c#,.net,vb.net,multithreading,atomic,C#,.net,Vb.net,Multithreading,Atomic,我完全理解Threading.Interlocked类提供的原子性;不过,我不明白为什么Add函数只提供两个重载:一个用于整数,另一个用于long。为什么不使用double或任何其他数字类型呢 显然,更改Double的预期方法是CompareExchange;我猜这是因为修改Double比修改整数更复杂。我仍然不清楚为什么,如果CompareExchange和Add都能接受整数,它们就不能同时接受双精度。我怀疑有两个原因 Net目标处理器仅支持整数类型的联锁增量。我相信这是x86上的锁前缀,其他
显然,更改Double的预期方法是CompareExchange;我猜这是因为修改Double比修改整数更复杂。我仍然不清楚为什么,如果CompareExchange和Add都能接受整数,它们就不能同时接受双精度。我怀疑有两个原因
Interlocated类围绕Windows API Interlocated**函数展开 反过来,它们围绕本机处理器API,使用x86的锁指令前缀。它仅支持在以下指令前加前缀: BT、BTS、BTR、BTC、XCHG、XADD、ADD、OR、ADC、SBB和、SUB、XOR、NOT、NEG、INC、DEC 您将注意到,这些方法反过来几乎映射到互锁方法。不幸的是,这里不支持非整数类型的ADD函数。64位平台支持64位长的Add
这是一篇很棒的文章。正如Reed Copsey所说,联锁操作(通过Windows API函数)映射到x86/x64处理器直接支持的指令。假设其中一个函数是XCHG,您可以执行原子XCHG操作,而不必真正关心目标位置的位代表什么。换句话说,代码可以“假装”正在交换的64位浮点数实际上是一个64位整数,而XCHG指令不会知道其中的差别。因此,.Net可以通过“假装”它们分别是整数和长整数,为浮点和双精度提供互锁的.Exchange函数 但是,所有其他操作实际上都对目标的各个位进行操作,因此除非这些值实际表示整数(在某些情况下是位数组),否则它们不会工作。其他操作已经解决了“为什么?”。但是,使用
compareeexchange
原语可以轻松地滚动您自己的添加(ref double,double)
:
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
的值设置为newValue
,前提是当前值等于currentValue
。因为它是以原子的、线程安全的方式实现的,所以我们可以单独依赖它,而不用使用锁
为什么while(true)
循环?这样的循环在实现优化并发算法时是标准的<如果当前值与当前值不同,则代码>比较交换不会更改位置1
。我将currentValue
初始化为location1
——执行非易失性读取(这可能会过时,但不会改变正确性,因为compareeexchange
将检查该值)。如果当前值(仍然)是我们从位置
读取的值,比较交换
会将该值更改为新值
。如果没有,我们必须使用新的当前值重试CompareExchange
,该值由CompareExchange
返回
如果该值被另一个线程更改,直到下一次
CompareExchange
再次执行时,它将再次失败,需要再次重试-理论上,这可能永远持续下去,因此会出现循环。除非您不断更改来自多个线程的值,CompareExchange
很可能只被调用一次,如果当前值仍然是对location1
的非易失性读取产生的值,或者是两次,如果它不同。关于您的第二点:是的,但我要问的是Interlocked.Add,不联锁。增量。你能再解释一下它是如何工作的吗?你为什么循环?“CompareExchange的作用是什么?”Mzn补充道。希望这能解释。这是一个很好的答案!至少现在我想我知道乐观并发意味着什么:“在编写并发代码时假设事情最终会顺利进行。”对于我们的while(true),这意味着我们不会永远循环(我们相当乐观)。非常感谢@MZN悲观并发:让我们锁定多个操作,因为它可能会出现多个步骤出错。可靠,并且是并发代码的可靠瓶颈。乐观并发:让我们使用CompareExchange
进行一次无锁的尝试,因为大多数情况下,它只能与多个线程一起工作,如果不能,我们将重试。这允许更多的并发性,并且几乎总是更快。编程不容易,有些事情没有锁就无法完成。@JamPicklenewCurrentValue=location1
不是线程安全的,因为它不插入内存屏障。由于.Net 4.5可以这样做,但读取内存屏障会导致性能损失,因此对于当前值为0
的情况,您将有一个用于易失性读取的内存屏障和一个用于compareeexchange
的内存屏障。但是,易失性读取应该比compareeexchange
快,但不确定.Net,因此对于0
以外的当前值,使用易失性读取会更快。链接不再处于活动状态。这是一份来自Internet存档的备份