.NET Events – Thread Safety and Reliability

In C#, you can easily publish events between classes and instances. All you need is to subscribe to the event, and give it a delegate that you want to be invoked by it. Seems almost too easy…

Events – a piece of cake?

Since the event will be null until somebody subscribes to it, we need to decide how we’ll protect our code from a NullReferenceException, before invoking the event.

The Commonly used solution for that specific problem is to check whether the event is null before invoking it:

public class Class1
{
	public event EventHandler ev;
	public void InvokeEvent()
	{
		if(ev != null)
			e.Invoke(this, EventArgs.Empty);

	}
}

Unfortunately, this solution can be problematic because it is not thread safe. the subscriber can unsubscribe between the condition and the invoking, and this will lead to a NullReferenceException when the event is invoked.

Consider the following scenario: Two tasks are running, one task invokes an event, while at the same time another task subscribes and unsubscribes from the same event. The first task enters the condition because the second task subscribed to the event. In the time between the condition and the invocation, the second task stops listening to that event, or in other words unsubscribes.

We can see that OzCode warns us that if we keep running the code, a NullReferenceException will be thrown.

2

For those who don’t know OzCode: it’s a debugging extension for Visual Studio that reduces debugging time and helps me debug my code painlessly. In this example, the ev parameter is colored in red. It means that this parameter is in fact null. This way I know immediately that there’s a problem – without needing to go over all of the relevant parameters with the mouse.

I am going to suggest two possible solutions, but please don’t use the first solution because there is a very dangerous bug that hides there.

Temporary assignment and why you should not use it

I’ve seen developers try to solve the race condition described above by using temporary assignment to a local variable. This will copy a reference to that event, and when the second task unsubscribes from the event it will not unsubscribe from that temporary variable.  When the invocation runs, it will run from the new variable, and a NullReferenceException will not be thrown.

3

The problem with this solution is that if we decide to unsubscribe from an event, it means that we do not want that delegate to be invoked anymore. Unfortunately using a local variable means that it will.

For example, I recently developed a software component that controlled a robot which had to perform a movement when a sensor publishes an event. I had an emergency button that stopped all movements when pressed, and we made sure to unsubscribe from all events once the button was pressed, to prevent any additional movements due to sensor events. Unfortunately, the button event would sometimes unsubscribe from the relevant event after that event was already copied to a local variable, causing a dangerous bug: robot movement could happen after the emergency button has been pressed!

Using the new C# 6 solution

If you use C# 6 and up, you can use the “?.” operator. That operator guarantees you that the method will run only if the object is not null:

public class Class1
{
	public event EventHandler ev;

	public void InvokeEvent()
	{
		ev?.Invoke(this, EventArgs.Empty);
	}
} 

The event will be invoked only if ev is not null. The disadvantage with this solution is that you can’t just call the event, you must check if it is null before every invocation. And on top of that it’s not thread safe, since the compiler creates code similar to the one in the previous solution.

My Favorite Solution

I think that the best pattern to use events in a thread safe manner, is to set an empty delegate as an initial listener to the event. In that case, you will not need to check if the event is null before every invocation. It’s thread safe, the code is cleaner, the event will always invoke the empty delegate, and will never be null.

public class Class1
{
	public event EventHandler ev = delegate { }; 
	public void InvokeEvent()
	{
		ev.Invoke(this, EventArgs.Empty);
	}
}

I find this solution to be simpler, and works as expected with very little overhead.

Conclusion

So, If you ask me (and if you read this post – I hope you do), the best pattern to deal with events is just to assign an empty delegate to the event. That’s the cleanest code, and most thread safe way to invoke the event. All the other patterns have potential issues that may not happen all the time, but are both disastrous and hard to debug.

So there you have it – events can be a wonderful way to publish notifications, but you must use them correctly if you don’t want to get weird behavior once you try to unsubscribe from an event of another thread or task.