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

本文关键字:AMD64 JIT 优化 CLR 调用 模式 事件 | 更新日期: 2023-09-27 18:35:14

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

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

在这种情况下,如果一个线程检查 MyEvent != null,另一个线程从事件中取消订阅处理程序,我们可以得到一个竞争条件。(然后MyEvent试图触发和操作。空引用异常)

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

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

这很好用,因为

委托

是不可变的;一旦创建,委托的调用列表不会更改。

但据我所知,AMD64 JIT 进行了一些优化,可以忽略本地副本并读取事件处理程序的实际值。(一篇文章很旧,但我找不到任何关于此类问题的实际信息)。

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

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

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

他断言应该使用挥发性并非完全不准确。 然而,这需要用C编译器眼镜来看待问题。 或者 x86 抖动实现易失的方式。 不幸的是,C#语言遭受了对易失性定义严重破坏的困扰,任意拉出来以处理具有弱内存模型的处理器。 安腾是那里的主要麻烦制造者。 搞砸了,让乔·达菲完全放弃并宣布它是邪恶的。

他们

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