C# 事件调用模式和CLR AMD64 JIT优化

C# 事件调用模式和CLR AMD64 JIT优化,c#,.net,multithreading,clr,jit,C#,.net,Multithreading,Clr,Jit,我们都知道在多线程环境中处理.NET事件时存在的问题。 其中之一是当我们试图调用事件而不复制到局部变量时: if (MyEvent != null) MyEvent(this, EventArgs.Empty); var handler = MyEvent; if (handler != null) handler(this, EventArgs.Empty); 在这种情况下,如果一个线程检查MyEvent!=null,另一个从事件中取消订阅处理程序。(然后MyEvent尝

我们都知道在多线程环境中处理.NET事件时存在的问题。 其中之一是当我们试图调用事件而不复制到局部变量时:

if (MyEvent != null)
    MyEvent(this, EventArgs.Empty);
var handler = MyEvent;
if (handler != null)
    handler(this, EventArgs.Empty); 
在这种情况下,如果一个线程检查MyEvent!=null,另一个从事件中取消订阅处理程序。(然后MyEvent尝试启动并运行..NullRefException)

解决方案(由J.Richter提出)是将事件处理程序复制到局部变量:

if (MyEvent != null)
    MyEvent(this, EventArgs.Empty);
var handler = MyEvent;
if (handler != null)
    handler(this, EventArgs.Empty); 
这很有效,因为

但据我所知,它可以忽略本地副本并读取事件处理程序的实际值。(一篇文章很旧,但我找不到任何关于这个问题的实际信息)


那么,CLR JIT在这种情况下究竟是如何工作的呢?会有NullReferenceException吗?

这篇博文不完整,没有说明他们对此做了什么。它很旧,发布于x64抖动实际发布前一年。他们可能在测试时发现了问题

他关于应该使用volatile的主张并非完全不准确。然而,这需要从C编译器的角度来看待这个问题。或者x86抖动实现的方式。不幸的是,C#语言严重破坏了volatile的定义,为了处理内存模型较弱的处理器而任意拉出。安腾是那里的主要麻烦制造者。搞砸了,让乔·达菲完全放弃了


他们提出的解决方案相当激烈,完全消除了对volatile的需求,对代码生成没有任何影响。事件触发模式是为骑行而保存的,x64抖动实际上复制并存储了引用。不是在一个局部变量中,而是在一个CPU寄存器中,x64有很多这样的变量。否则,这是一个标准的优化器功能。

IMO事件完全不适合多线程环境。@spender为什么建议这样做?事件在线程化环境中仍然非常有用,只要您正确使用它们。许多线程组件完全基于事件…优化是非法的,还是CLR有效地扩展了规范以使此模式安全?如果您使volatile表示您喜欢的意思,那么它就不是非法的。就像博客作者那样。这里不涉及CLR,这是一个纯粹的抖动细节。