Events and Threads (Part 2)

It’s time to continue our discussion of events and threads. You’ll note in the last post that I didn’t say much about “thread safe” events, because it’s not clear what that would mean, particularly as regards the raising of an event. You won’t see much in this post about “thread safe” events, either, though I do hope to get to that eventually.

We’ve already talked about adding and removing an event handler, so it’s only natural that we would now talk about raising the event. The most commonly discussed problem that we face when raising an event in C# is that the event delegate is null if there are no subscribers.

    public event EventHandler Click;
    private void RaiseClick()
    {
        // throws NullReferenceException if no subscribers

        Click(this, EventArgs.Empty);
    }

In fact, I touched on this subject back in March, where I noted that assigning a do-nothing event handler to the event delegate avoids that problem entirely, though it does add a bit of inefficiency.

    public event EventHandler Click = delegate { };
    private void RaiseClick()
    {
        // never throws NullReferenceException

        Click(this, EventArgs.Empty);
    }

If your class has thread affinity, you must only raise the event from the UI thread, so you can safely do a null check without worrying about another thread removing the last event handler between the check and the call.

    public event EventHandler Click;
    private void RaiseClick()
    {
        VerifyAccess();
        if (Click != null)
            Click(this, EventArgs.Empty);
    }

If your class is thread-compatible, it must be assumed that you only raise an event from the thread that is currently accessing your instance, so, again, you can safely do a null check without worrying about other threads.

    public event EventHandler Click;
    private void RaiseClick()
    {
        if (Click != null)
            Click(this, EventArgs.Empty);
    }

But what if you want to raise an event in response to background work on a worker thread? In the case of a thread-affined class, there is usually a way to submit work to the UI thread, allowing you to raise the event from the UI thread. In WPF, you can use the Dispatcher for the UI thread.

    public event EventHandler Click;
    private void RaiseClick()
    {
        Dispatcher.Invoke(DispatcherPriority.Send, new SendOrPostCallback(
            delegate
            {
                if (Click != null)
                    Click(this, EventArgs.Empty);
            }), null);
    }

In Windows Forms or WPF, you can use the SynchronizationContext of the UI thread.

    public event EventHandler Click;
    private void RaiseClick()
    {
        m_context.Send(
            delegate
            {
                if (Click != null)
                    Click(this, EventArgs.Empty);
            }, null);
    }
    SynchronizationContext m_context = SynchronizationContext.Current;

Raising an event in response to background work on a worker thread for a thread-compatible class is more interesting, because subscribers to the event will be called on an arbitrary thread. Therefore, for all intents and purposes, the event must be thread-safe, because it could be subscribed or unsubscribed on one thread and raised on another thread at the same time.

Which means that it’s time to talk about thread-safe events, but I think I’ll save that discussion for a future post.

Posted by Ed Ball on May 23, 2008