【问题标题】:Raising Events in Multi-Threaded Environment [duplicate]在多线程环境中引发事件 [重复]
【发布时间】:2013-08-28 22:51:22
【问题描述】:

从 .NET 4.0 开始,自动生成的添加/删除事件处理程序是线程安全的(herehere)。因此,将其侦听器注册到公开事件的客户端可以从多个线程同时执行此操作,而无需竞争。

但是如果我想以线程安全的方式触发事件呢?推荐的做法似乎如下(here):

public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
    EventHandler myEvent = MyEvent;
    if (myEvent != null)
    {
        myEvent(this, e);
    }
}

但是,在阅读了有关 .NET 内存模型的内容(例如 MSDN 杂志 2012-122013-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 规范真正允许用重复的内存读取来替换局部变量。
  • 再添加两个我自己的问题的副本 ‒ this 尤其是 this,Eric Lippert 给出了很好的回答。

标签: c# .net multithreading concurrency memory-model


【解决方案1】:

您必须添加唯一的行以使第一个 sn-p 在多线程环境中正确:

public event EventHandler MyEvent;
protected void OnMyEvent(EventArgs e)
{
    EventHandler myEvent = MyEvent;
    Thread.MemoryBarrier();
    if (myEvent != null)
    {
        myEvent(this, e);
    }
}

内存屏障拒绝重新排序编译器和 CPU 的读写操作。这就是易失性读/写的实现方式。你可以阅读更多about memory barrier here

【讨论】:

  • 我的理解是内存屏障是禁止对内存读写进行重新排序的栅栏(例如,通过编译器或处理器缓存)。然而,这并不意味着内存屏障也会影响内存读取被引入/消除的方式。请提供建议或指向我的参考资料,以支持您的观点。
  • 编译器和处理器不给我们任何保证,这个代码会产生多少物理写入和读取。它甚至可能因呼叫而异。是的,内存屏障不会影响这种行为。这意味着没有 MB 对事件委托的引用可以从任何位置获取:内存、缓存、寄存器。内存和缓存被许多线程修改。这就是 NRE 成为可能的原因。内存屏障强制编译器在线程堆栈中存储对委托的引用。其他线程无法修改它。这样可以安全地检查和调用。
  • 感谢陪审团对我的支持,但是将值从堆移动到线程的堆栈对我来说没有意义 - 然后值将如何返回堆?此外我相信ECMA标准没有提到任何这样的操作。
猜你喜欢
  • 2020-07-11
  • 1970-01-01
  • 2014-10-05
  • 1970-01-01
  • 2012-06-26
  • 2018-02-23
  • 2021-12-09
  • 1970-01-01
  • 2017-11-23
相关资源
最近更新 更多