事件提升和.net 4.5中的阅读介绍

时间:2013-04-23 06:49:18

标签: c# .net multithreading events

我有与此MSDN杂志相关的问题article

  

阅读简介正如我刚才解释的那样,编译器有时会融合   多次读入一个。编译器也可以拆分单个读取   多次读取。在.NET Framework 4.5中,阅读介绍是   比阅读消除更不常见,只发生在非常罕见的情况下,   具体情况。但是,它有时会发生。

public class ReadIntro {
  private Object _obj = new Object();
  void PrintObj() {
    Object obj = _obj;
    if (obj != null) {
      Console.WriteLine(obj.ToString());
    // May throw a NullReferenceException
    }
  }
  void Uninitialize() {
    _obj = null;
  }
}
  

如果检查PrintObj方法,它看起来就像是obj值   在obj.ToString表达式中永远不会为null。但是,那行   代码实际上可能会抛出NullReferenceException。 CLR JIT可能会   编译PrintObj方法,好像它是这样编写的:

void PrintObj() {
  if (_obj != null) {
    Console.WriteLine(_obj.ToString());
  }
}

但这不是处理事件的模式吗?!

void RaiseEvent()
{
    var myEvent = MyEvent;
    if (myEvent != null)
    {
         myEvent(this, EventArgs.Empty);
    }
}

我是否想念一些重要的事情?

1 个答案:

答案 0 :(得分:5)

这篇文章让我感到困惑,我做了一些研究。我发现了两种思想流派。

1。有人说模式是安全的

因为CLR 2.0内存模型比1.x更严格并禁止它。

  

"读取和写入无法引入",MSDN Magazine(10月05日),文章了解低锁技术对多线程应用程序的影响

     

" .NET内存模型禁止它[读取介绍]用于引用GC堆内存的普通变量",Joe Duffy,书 Windows上的并发编程,pp517-8。

[注意:Joe Duffy基本上说的是同样的东西但是留下了在堆栈上读取引入的可能性,因此不能共享因此是安全的]

我找到那些" .NET 2.0内存模型"解释很奇怪。我已经阅读了2012 ECMA CLI规范以及C#标准,没有发现禁止读取引入的声明。他们不太可能在2.0和4之间削弱内存模型。(??)

另一方面,我相信JIT团队已经意识到这些模式并且不会破坏它们,至少在x86上......但是说这与它在标准中的说法不一样。团队决策可能在将来或其他平台上发生变化。

编辑请不要错过Eric Lippert的评论:"没有阅读介绍"是 Microsoft CLI实现的承诺。在ECMA标准中没有任何关于它的内容,并且在使用其他实现(例如Mono)时,所有投注均已关闭。 结束编辑

2。有人说它不安全

具体来说:Igor Ostrovsky在你引用的文章中,以及Stephen Toub在这篇博文的评论中发表的讨论:http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx

基本上他们说读取引入或删除是一种常见的编译器优化,如果他们不改变单线程行为,C#和JIT都可以这样做。

[注意:Eric Lippert已经说过C#编译器目前没有进行这样的优化。]

请注意,Igor似乎意识到JIT非常保守,并在文章中明确指出您的示例代码不会破坏x86-x64上的.NET 4.5中的 。另一方面,他说在其他情况下它可能会中断,如果它是更复杂的代码模式,未来或过去的.net版本,或其他平台,则可能会有所不同。

解决方案

如果您想100%安全,解决方案是使用易失性读取。易失性读写被C#标准定义为副作用,因此无法引入或删除它们。

ECMA CLI标准有一个类似的明确声明,即不删除易失性读写。

关于线程安全事件的说明

正如许多人所指出的那样,线程安全性不仅仅是提升代码的事件。您的事件处理程序应该在取消订阅后准备好被称为

我同意Hans Passant的观点,即最好的指导是“不要做”#34;但有时你需要这样做。在这些情况下,请确保您的事件处理程序代码也是线程安全的。在这些情况下,您可能还需要考虑一种更简单的基于锁的同步方法。