【发布时间】:2013-08-28 22:51:22
【问题描述】:
从 .NET 4.0 开始,自动生成的添加/删除事件处理程序是线程安全的(here 和 here)。因此,将其侦听器注册到公开事件的客户端可以从多个线程同时执行此操作,而无需竞争。
但是如果我想以线程安全的方式触发事件呢?推荐的做法似乎如下(here):
public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
EventHandler myEvent = MyEvent;
if (myEvent != null)
{
myEvent(this, e);
}
}
但是,在阅读了有关 .NET 内存模型的内容(例如 MSDN 杂志 2012-12 和 2013-01)后,我不再认为这是正确的。我担心的是编译器可能会引入内存读取,因此上面的代码可能会被 JIT 编译成这样的:
public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
// JIT removed the local variable and introduced two memory reads instead.
if (MyEvent != null)
{
// A race condition may cause the following line to throw a NullReferenceException.
MyEvent(this, e);
}
}
删除局部变量并使用重复的内存读取是合法的,因为如果在单线程环境中执行它不会改变方法的行为。这是由 ECMA 规范 (ECMA-335: I.12.6.4) 提供的。 MSDN 杂志的2013-01 期也提供了可理解的示例。
我在这里遗漏了什么吗?如果不是,请提供解决方法。
【问题讨论】:
-
感谢您指出这一点。我搜索了 SO 和 Internet,但我显然错过了那个帖子。加上我的两分钱,我实际上认为您所指的问题选择了错误的答案。正确的似乎是这个 - stackoverflow.com/a/2365885/385737。 ECMA 规范真正允许用重复的内存读取来替换局部变量。
标签: c# .net multithreading concurrency memory-model