C# 在dapper.net方法中,Interlocated.CompareExchange用于什么?

C# 在dapper.net方法中,Interlocated.CompareExchange用于什么?,c#,.net,thread-safety,dapper,C#,.net,Thread Safety,Dapper,在Link.TryAdd方法中的dapper代码中,有以下代码段: var snapshot = Interlocked.CompareExchange(ref head, null, null); 为什么这是必需的而不是简单的: var snapshot = head; 两行都不会更改头部的值,两行都将头部的值分配给快照。为什么选择第一个而不是第二个 Edit:我指的代码在这里:他们想要执行volatile读取,但是没有采用泛型类型参数的线程的重载。使用Interlocked.compar

Link.TryAdd
方法中的dapper代码中,有以下代码段:

var snapshot = Interlocked.CompareExchange(ref head, null, null);
为什么这是必需的而不是简单的:

var snapshot = head;
两行都不会更改
头部的值
,两行都将
头部的值分配给
快照
。为什么选择第一个而不是第二个


Edit:我指的代码在这里:

他们想要执行volatile读取,但是没有采用泛型类型参数的
线程的重载。使用
Interlocked.compareeexchange
这种方法可以获得相同的结果

他们试图解决的问题是,如果JIT编译器认为合适的话,它可以优化对临时变量的赋值。如果另一个线程在当前线程在一系列操作中使用head引用时对其进行变异,则可能会导致线程问题

编辑:


问题不是在
TryAdd
的开头读取过时的值。问题在于,在第105行,他们需要将当前磁头与前一磁头进行比较(保存在
快照中)。如果存在优化,则不存在保存先前值的
快照
变量,此时将再次读取
头部
。很可能,
CompareExchange
成功,即使头可能在第103行和第105行之间发生了更改。结果是,如果两个线程同时调用
TryAdd
,列表中的一个节点将丢失。

mike z是正确的:这将阻止(合法的)JIT优化破坏代码

不过,他们可以使用volatile结构技巧:读取
head
,并将其分配给某个结构的volatile字段。接下来,从该字段读取它,它保证是一个易失性读取

结构本身根本不重要。重要的是使用了volatile字段来复制变量

就像这样:

struct VolatileHelper<T> { public volatile T Value; }
...
var volatileHelper = new VolatileHelper<Field>();
volatileHelper.Value = head;
var snapshot = volatileHelper.Value;
struct volatilehelp{public volatile T Value;}
...
var volatileHelper=新的volatileHelper();
挥发帮助值=压头;
var snapshot=volatileHelper.Value;
希望它没有运行时成本。在任何情况下,成本都低于导致CPU内存一致性通信的联锁操作


事实上,每个缓存访问(甚至是读取缓存)都需要内存一致性流量,这使得它成为一个慢缓存!联锁操作是一种系统全局资源,不能随更多CPU扩展。联锁访问使用全局硬件锁(每个内存地址,但这里只有一个地址)。

我在问题正文中添加了代码链接。我想了解为什么这种优化在这种情况下很重要。好的,假设作业是优化的。这实际上意味着snapshot变量可能会得到一个过时的值。那么,有什么大不了的,代码中的第105-106行通过重新尝试解决了这个特殊问题,如果是这样的话。还要注意,第105行将强制变量(
head
)刷新,以便代码不会因为优化而卡在循环中。在这个特定的例子中,当代码中断时,你能描述一个场景吗?是的,很有意义。在接受答案之前,我会仔细考虑一下。谢谢。我认为这不会完全起作用,因为head是通过ref传递的。因此分配
volatileHelper.Value=head
将遇到与分配相同的问题。即使volatileHelper.值是不稳定的,头部的变化也不会被检测到。我错了吗?@zespri,为什么他们会“不被发现”?顺便说一句,这个技巧是由.NET4.5中的BCL使用的。这个类被称为“Volatile…”之类的东西,因为head本身不是Volatile。