Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
155 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...