事件认识和了解引入.NET 4.5 [英] Event raising and Read introduction in .net 4.5

查看:227
本文介绍了事件认识和了解引入.NET 4.5的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有与此相关的MSDN杂志文章的问题。

  

阅读介绍正如我刚才解释,编译器有时保险丝   多读成一个。编译器还可以拆分一个读   成多次读取。在.NET Framework 4.5,阅读介绍是   比读消除和只发生在极少数不太常见,   具体情况。但是,它有时会发生。

 公共类ReadIntro {
  私有对象_OBJ =新的对象();
  无效PrintObj(){
    obj对象= _OBJ;
    如果(OBJ!= NULL){
      Console.WriteLine(obj.ToString());
    //可能会抛出一个NullReferenceException
    }
  }
  无效取消初始化(){
    _OBJ = NULL;
  }
}
 

  

如果您检查PrintObj方法,它看起来像OBJ值会   永远在obj.ToString EX pression空。但是,该行的   code实际上可能抛出一个NullReferenceException。 CLR的JIT威力   编译PrintObj方法,就好像它是这样写的:

 无效PrintObj(){
  如果(_OBJ!= NULL){
    Console.WriteLine(_obj.ToString());
  }
}
 

不过,是不是处理事件的模式?!

 无效的RaiseEvent()
{
    VAR myEvent = MyEvent;
    如果(myEvent!= NULL)
    {
         myEvent(这一点,EventArgs.Empty);
    }
}
 

难道我错过了一些东西重要?

解决方案

这篇文章让我困惑,以及和我做了一些研究。我发现了两个思想流派。

1。有人说该模式是安全的

由于在CLR 2.0的内存模型比1.x的更加严格,并禁止它。

  

读与写不能出台,MSDN杂志(10月5),文章的了解小锁技术在多线程应用程序

的影响      

在.NET内存模型禁止它[读简介】对于普通的变量指的GC堆内存,乔·达菲,本书的在Windows并发编程的,pp517-8。

[注:乔·达菲基本上说同样的事情,但留下的读取介绍堆栈,这是不共享的,因此安全上的可能性]

我觉得那些.NET 2.0的内存模型的解释很奇怪。我已阅读2012 ECMA CLI规范以及C#的标准,并没有发现引用声明禁止读取的介绍。这是不可能的,他们之间的削弱2.0和4(?)

内存模型

在另一方面,我认为,JIT小组已经注意到这些模式并不会打破他们,至少在86 ...但说这是不一样的话说,它是标准内。该小组决定可能在未来改变或其他平台。

修改千万不要错过下面埃里克利珀的评论:不读引进是的微软的CLI实现的承诺。有一个在ECMA标准一无所知,并使用在其他的实现(如单声道)全盘皆输。 END修改

2。有人说这是不安全的

具体做法是:伊戈尔·奥斯特洛夫斯基在文章中你引用,以及斯蒂芬Toub在这篇博客的评论里讨论:的http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx.

基本上他们说,读引进或消除的是,C#和JIT都被允许这样做,如果他们不改变单线程的行为共同的编译器优化。

[注:埃里克利珀曾表示,C#编译器是不是现在在做这样的优化]

需要注意的是伊戈尔似乎也意识到了JIT是相当保守的,明确地指出了,你的样品code不会打破的在.NET 4.5基于x86的64位的文章。在另一方面,他说,这可能在其他情况下突破,没有precising如果是更复杂的code图案,过去或未来的.NET版本,或其他平台。

解决方案

如果你想成为100%的安全,解决的办法是使用挥发性读取。挥发性读取和写入被定义为副作用由C#标准,因此它们不能被引入,也不能被删除。

在ECMA CLI标准大约有没有去除挥发性读取和写入了类似的明确声明。

有关线程安全事件的说明

正如许多人所指出的,还有更多的线程安全不仅仅是活动筹集code。事件处理程序应该是准备好被调用的已取消订阅后的。

我同意汉斯帕桑特,最好的指导是不这样做,但有时你需要。在这种情况下,只是确保你的事件处理程序code也是线程安全的。在这种情况下,你可能还需要考虑一个简单的基于锁的方法来同步。

I have the question related to this MSDN Magazine article.

Read Introduction As I just explained, the compiler sometimes fuses multiple reads into one. The compiler can also split a single read into multiple reads. In the .NET Framework 4.5, read introduction is much less common than read elimination and occurs only in very rare, specific circumstances. However, it does sometimes happen.

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;
  }
}

If you examine the PrintObj method, it looks like the obj value will never be null in the obj.ToString expression. However, that line of code could in fact throw a NullReferenceException. The CLR JIT might compile the PrintObj method as if it were written like this:

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

But isn't it a pattern of working with events?!

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

Do I miss something important here?

解决方案

This article got me confused as well and I did some research. I found two schools of thought.

1. Some say the pattern is safe

Because the CLR 2.0 memory model was more strict than 1.x and prohibits it.

"Reads and writes cannot be introduced", MSDN Magazine (Oct 05), article Understand the Impact of Low-Lock Techniques in Multithreaded Apps.

"the .NET memory model prohibits it [read introduction] for ordinary variables referring to GC heap memory", Joe Duffy, book Concurrent Programming on Windows, pp517-8.

[Note: Joe Duffy basically says the same thing but leaves the possibility of read introductions on the stack, which is not shared hence safe]

I find those ".NET 2.0 memory model" explanations strange. I have read the 2012 ECMA CLI specification as well as the C# standard and found no reference to a statement prohibiting read introduction. It's unlikely that they weakened the memory model between 2.0 and 4. (??)

On the other hand, I believe that the JIT team is aware of those patterns and will not break them, at least on x86... but saying this is not the same as saying that it's in the standard. The team decision may change in the future or on other platforms.

EDIT Don't miss Eric Lippert's comment below: "no read introduction" is a promise of Microsoft CLI implementation. There's nothing about it in the ECMA standard and when using other implementations (e.g. Mono) all bets are off. END EDIT

2. Some say it's unsafe

Specifically: Igor Ostrovsky in the article you've quoted, as well as Stephen Toub in a discussion inside the comments of this blog post: http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx.

Basically they say that read introduction or elimination is a common compiler optimization that both the C# and the JIT are allowed to do if they don't change the single-threaded behavior.

[Note: Eric Lippert has said that the C# compiler is not doing such optimizations at the moment.]

Note that Igor seems to be aware that the JIT is quite conservative and explicitely points out in the article that your sample code will not break in .NET 4.5 on x86-x64. On the other hand he says that it may break in other cases, without precising if it's more complex code patterns, future or past .net releases, or other platforms.

Solution

If you want to be 100% safe, the solution is to use a volatile read. Volatile reads and writes are defined as side-effects by the C# standard, hence they can't be introduced nor removed.

The ECMA CLI standard has a similar explicit statement about not removing volatile reads and writes.

A note about thread-safe events

As many have pointed out, there is more to thread-safety than just the event raising code. Your event handler should be ready to be called after it has unsubscribed.

I agree with Hans Passant that the best guidance is "don't do it", but sometimes you need to. In those cases just be sure that your event handler code is also thread safe. In those cases you might also want to consider a simpler lock-based approach to synchronisation.

这篇关于事件认识和了解引入.NET 4.5的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆