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

时间:2013-10-14 16:46:59

标签: c# .net multithreading clr jit

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

if (MyEvent != null)
    MyEvent(this, EventArgs.Empty);

在这种情况下,如果在一个线程检查MyEvent!= null之后,我们可以获得竞争条件,另一个是从事件中取消订阅处理程序。 (然后MyEvent尝试触发和操作.. NullRefException)

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

var handler = MyEvent;
if (handler != null)
    handler(this, EventArgs.Empty); 

这很有效,因为

  

Delegates are immutable; once created, the invocation list of a delegate does not change.

但我知道AMD64 JIT does some optimizations可以忽略本地副本并读取事件处理程序的实际值。 (一篇文章陈旧但我找不到任何关于此类问题的实际信息。)

那么,在这种情况下CLR JIT实际上是如何工作的?可以有NullReferenceException吗?

1 个答案:

答案 0 :(得分:2)

博客文章不完整,它没有讲述他们对此做了什么。它是旧的,在x64抖动实际发布前一年发布。他们可能在测试时发现了这个问题。

他断言应该使用 volatile 并不完全不准确。然而,这需要用C编译器眼镜来查看问题。或者x86抖动实现volatile的方式。不幸的是,C#语言严重破坏了 volatile 的定义,随意拉出以处理具有弱内存模型的处理器。 Itanium是那里的主要麻烦制造者。搞砸了足以让Joe Duffy完全放弃并declare it evil

他们提出的解决方案相当激烈,他们完全消除了对 volatile 的需求,它根本不会影响代码生成。并且事件发射模式在骑行中被拯救,x64抖动实际上确实复制并存储了参考。不是在局部变量中,而是在CPU寄存器中,x64有很多。否则是标准优化器功能。