Tom*_*mas 5 .net c# concurrency multithreading memory-model
从 .NET 4.0 开始,自动生成的添加/删除事件处理程序是线程安全的(此处和此处)。因此,将其侦听器注册到公开事件的客户端可以从多个线程同时进行,而不会发生竞争。
但是如果我想以线程安全的方式触发事件怎么办?推荐的做法似乎如下(这里):
public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
EventHandler myEvent = MyEvent;
if (myEvent != null)
{
myEvent(this, e);
}
}
Run Code Online (Sandbox Code Playgroud)
但是,在阅读了有关 .NET 内存模型的内容后(例如 MSDN 杂志2012-12和2013-01),我不再认为这是正确的。我担心的是编译器可能会引入内存读取,因此上面的代码可能会被 JIT-ted 变成这样:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
删除局部变量并使用重复的内存读取是合法的,因为如果在单线程环境中执行它不会改变方法的行为。这是 ECMA 规范 ( ECMA-335: I.12.6.4 )。MSDN杂志2013-01期也提供了可理解的例子。
我在这里错过了什么吗?如果没有,请建议解决方法。
小智 2
您必须添加唯一的一行以使第一个片段在多线程环境中正确:
public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
EventHandler myEvent = MyEvent;
Thread.MemoryBarrier();
if (myEvent != null)
{
myEvent(this, e);
}
}
Run Code Online (Sandbox Code Playgroud)
内存屏障拒绝对编译器和 CPU 的读写重新排序。这就是易失性读/写的实现方式。您可以在此处阅读有关内存屏障的更多信息。
| 归档时间: |
|
| 查看次数: |
1401 次 |
| 最近记录: |