Checking for Null Before Event Dispatching... Thread Safe

Checking for null before event dispatching... thread safe?

In C# 6.0 you can use monadic Null-conditional operator ?. to check for null and raise events in easy and thread-safe way.

SomeEvent?.Invoke(this, args);

It’s thread-safe because it evaluates the left-hand side only once, and keeps it in a temporary variable. You can read more here in part titled Null-conditional operators.

Is C#'s null-conditional delegate invocation thread safe?

from MSDN:

The new way is thread-safe because the compiler generates code to
evaluate PropertyChanged one time only, keeping the result in
temporary variable. You need to explicitly call the Invoke method
because there is no null-conditional delegate invocation syntax
PropertyChanged?(e). There were too many ambiguous parsing situations
to allow it.

https://msdn.microsoft.com/en-us/library/dn986595(v=vs.140).aspx

Why does this common idiom for thread-safe event-calling in C# works at all?

The delegate object is immutable, so a copy of the reference to it is fine. A standalone local copy of a reference to an immutable object is pretty much the gold standard for avoiding a thread-race problem.

When you add/remove an event subscription, Delegate.Combine et al create a new delegate instance every time it changes (or null if you unsubscribe the last handler) and assigns a reference (/ null) to that new object to the backing field. That is why the snapshot is helpful.

BTW : in modern C#, you can just use TheEvent?.Invoke(....), which does this for you.

Why should I check for null before I invoke the custom event?

An OnXYZ method should always follow this form:

public void OnXYZ()
{
var evt = XYZ;
if (evt != null)
evt(sender, e); // where to get e from differs
}

There are a couple of reasons for this form:

  1. The if evt != null check ensures that we don't try to invoke a null delegate. This can happen if nobody has hooked up an event handler to the event.
  2. In a multithreaded scenario, since delegates are immutable, once we've obtained a local copy of the delegate into evt, we can safely invoke it after checking for non-null, since nobody can alter it after the if but before the call.

What to pass for e differs, if you need to pass an EventArgs descendant with a parameter there are two ways:

public void OnXYZ(string p)
{
var evt = XYZ;
if (evt != null)
evt(sender, new SomeEventArgs(p));
}

or more commonly this:

public void OnXYZ(SomeEventArgs e)
{
var evt = XYZ;
if (evt != null)
evt(sender, e);
}

This syntax:

evt(sender, e);

is just a different way of writing this:

evt.Invoke(sender, e);

Also note that externally to your class, the event is an event, you can only add or remove event handlers from it.

Internal to your class, the event is a delegate, you can invoke it, check the target or method, walk the list of subscribers, etc.


Also, in C# 6 there is a new operator introduced, ?. - the Null-conditional operator - which is basically short for if not-null, dereference, which can shorten this method:

public void OnXYZ(SomeEventArgs e)
{
var evt = XYZ;
if (evt != null)
evt(sender, e);
}

into this:

public void OnXYZ(SomeEventArgs e)
{
XYZ?.Invoke(sender, e);
}

which can be further shortened with the use of Expression-bodied members:

public void OnXYZ(SomeEventArgs e) => XYZ?.Invoke(sender, e);

Please note that it is not possible to write this:

XYZ?.(sender, e);

so you must in this case use Invoke yourself.

Thread Safe Event Handling

If it were

if (mNotification!=null)
{
mNotification(this, null);
}

mNotification could be set to null by another thread between if (mNotification!=null) and mNotification(this, null);

Why is an additional if statement to check if an event is null before invoking it?

If the event is not subscribed by the consumer of the class then invoking the event would raise exception as PersonArrived is null if not subscribed.

Raise event thread safely - best practice

There is a tiny chance that SomethingHappened becomes null after the null check but before the invocation. However, MulticastDelagates are immutable, so if you first assign a variable, null check against the variable and invoke through it, you are safe from that scenario (self plug: I wrote a blog post about this a while ago).

There is a back side of the coin though; if you use the temp variable approach, your code is protected against NullReferenceExceptions, but it could be that the event will invoke event listeners after they have been detached from the event. That is just something to deal with in the most graceful way possible.

In order to get around this I have an extension method that I sometimes use:

public static class EventHandlerExtensions
{
public static void SafeInvoke<T>(this EventHandler<T> evt, object sender, T e) where T : EventArgs
{
if (evt != null)
{
evt(sender, e);
}
}
}

Using that method, you can invoke the events like this:

protected void OnSomeEvent(EventArgs e)
{
SomeEvent.SafeInvoke(this, e);
}

Can I use null conditional operator instead of classic event raising pattern?

Yes

See Null-conditional Operators on MSDN.

There is an example covering what you ask

Without the null conditional operator

var handler = this.PropertyChanged;
if (handler != null)
handler(…)

With the null conditional operator

PropertyChanged?.Invoke(e)

The new way is thread-safe because the compiler generates code to evaluate PropertyChanged one time only, keeping the result in temporary variable.

Event handler and null-conditional operator


should I copy event ProperyChanged to local variable, if I use null-conditional operator?

No, there's no need. In fact, one of the main reasons the null-conditional operator was introduced was to simplify code using this pattern. It has the same effect as copying the source value to a local variable and inherently avoids the "check and use" concurrency trap that the "copy to local variable" technique is intended to address.

See related posts:

Invoking Events, h(args) vs EventName?.Invoke() (almost an exact duplicate…it does approach the question from a slightly different angle though)

Why should I check for null before I invoke the custom event?

Raising C# events with an extension method - is it bad?

Is there any reason to assign an event to a local variable before raising it?

What is the (event-dispatch) thread safe usage for JOptionPane.showMessageDialog and swing.utils.invokeAndWait?

Take a look at groovy.swing.SwingBuilder, it encapsulates invokeAndWait and invokeLater. Your example can be written as:

import groovy.swing.SwingBuilder
import javax.swing.*
import java.awt.*

def swing = new SwingBuilder()
def myMainFrame = new Frame()

swing.edt {
JOptionPane.showMessageDialog(
myMainFrame, "Hello There");
}


Related Topics



Leave a reply



Submit