LA.NET [EN]

Nov 23

In one of the previous posts, we’ve looked at the basics associated with .NET events. As promised, we’ll start improving our initial code and today we’ll talk about two topics:

  1. lambdas aren’t always your friends.
  2. we live in a multithreaded world.

Lets start with number 1…In the previous code, I’ve uses something like this to handle the event:

var std = new Student( );
std.StudentNameChanged +=
    ( sender, e ) => Console.WriteLine( "{0} — {1}", e.OldName, e.NewName );

Before going on, you should know that I do use this approach in 90% of the scenarios. The problem with it is that you cannot cancel a previous subscription by using this approach. Here’s some code which I’ve seen people use in the past:

var std = new Student( );
std.StudentNameChanged +=
    ( sender, e ) => Console.WriteLine( "{0} — {1}", e.OldName, e.NewName );
std.Name = "Luis";
std.StudentNameChanged -=
    ( sender, e ) => Console.WriteLine( "{0} — {1}",e.OldName,e.NewName );
std.Name = "Luis2";

The idea of the previous code is simple (though utterly wrong): we subscribe an event through a lambda and then we cancel it by passing the same lambda expression. Well, the problem is that the second lambda expression is different from the first. In this case, the easiest approach is to create a method compatible with the event’s type and then use that to subscribe/cancel the event handler:

static  void PrintName(Object sender, StudentNameChangedEventArgs e) {
    Console.WriteLine( "{0} — {1}", e.OldName, e.NewName );
}

And then, we can simply subscribe/cancel the event like we did in the old days:

var std = new Student( );
std.StudentNameChanged += PrintName;
std.Name = "Luis";
std.StudentNameChanged -= PrintName;

With 1 tackled, lets proceed to 2. It’s safe to assume that multithreading is here to stay and that means writing safer code. Let’s recover the code we used to fire the event:

protected virtual void OnNameChanged(StudentNameChangedEventArgs e) {
    if( StudentNameChanged != null ) {
        StudentNameChanged( this, e );
    }
}

Everyone who has had to write multithreaded programs will automatically cringe while reading the previous code. The problem is that between the test and the event execution, the thread can be stopped while another thread removes the existing delegate chain from the event field. And that’s why you’ll typically see the previous code re-written like this:

protected virtual void OnNameChanged(StudentNameChangedEventArgs e) {
    var aux = StudentNameChanged;
    if( aux != null ) {
        aux( this, e );
    }
}

The idea is simple: since StudentNameChanged is copied into aux, aux will always reference the same delegate chain that existed at the time of the copy. From that point on, we’ll be using aux for testing and firing the event and we can be sure the aux’s value won’t changed between the test and the execution. Since delegates are immutable, then we’re safe, right?

Unfortunately, we’re not…I’ve used similar code to this for a long time until I’ve learnt that the compiler may optimize (though it currently doesn’t do it) the previous code and simply drop the aux reference. When that happens, we end up with the initial code which exhibits the racing behavior I’ve mentioned before. Bottom line, we need safer code. In these scenarios, the best option I’ve seen is presented by the excellent CLR via C#, by Jeffrey Richter, and consists on using the CompareExchange method:

protected virtual void OnNameChanged(StudentNameChangedEventArgs e) {
    var aux = Interlocked.CompareExchange( ref StudentNameChanged, null, null );
    if( aux != null ) {
        aux( this, e );
    }
}

In the previous snippet, CompareExchange will only change the StudentNameChanged event to null *when* it’s null (in other words, it will never change its value if that value is not null). The advantage of using this method is that it will always return a reference to the StudentNameChanged event in a thread safe way. With this small performance hit (yep, there’s a small cost associated with using this method), we’re really safe. As I’ve said before, the compiler doesn’t currently perform the optimization which might break our second version of the code, so you might keep using that approach. Anyway, if you’re writing long-lived code, then you probably should play it safe and go with the more robust version.

In the next post, we’ll still keep looking at events and see how we can improve event declaration for classes that expose lots of events.

1 comment so far

  1. priya
    5:04 am - 12-17-2010

    It was my wife who first suggested we try and meet other couples for some fun and games, at first I went into shock but then thought about it and eventually became comfortable with the idea. We joined a dating website and uploaded some non specific personal information and then got involved in the online chat and webcam chat rooms. So far so good, we have a new spin on our relationship now.
    ========================
    Sydney swingers

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>