views:

120

answers:

3

I'm sure I've seen this around before but I'm wondering how I should raise an event thread-safely.

I have a message despatch thread which looks somthing like.

while(_messages > 0){

    Message msg;
    // get next message

    if (MessageDispatched != null)
        MessageDispatched(this, new MessageDispatchedEventArgs(msg.Msg, msg.Params));
}

I can see that it may be posible for MessageDispatched to be come null after the check. from a MS blog I've seen:

var handler = MessageDispatched;

if (handler != null)
    handler(this, new MessageDispatchedEventArgs(msg.Msg, msg.Params));

Which does stop the possibility of the reference becoming null after the check occurring. I'm wondering how to handle the case where the delegate is disposed (or even if it can?)

Should I just need to stick a try / catch around it, as its probably very rarely going to occur?

Edit

After reading answers I've considered making my class to handle this - quickly it looks something whats below, but I'm running into a few issues which make it not as clean as I want - maybe someone has an idea how to do that?

public class ThreadSafeEvent<TDelegate> 
// where TDelegate : Delegate        why is this a non allowed special case??? 
{
    List<TDelegate> _delegates = new List<TDelegate>();

    public void Subscribe(TDelegate @delegate)
    {

        lock (_delegates)
        {
            if (!_delegates.Contains(@delegate))
                _delegates.Add(@delegate);
        }
    }

    public void Unsubscibe(TDelegate @delegate)
    {
        lock (_delegates)
        {
            _delegates.Remove(@delegate);
        }
    }


    // How to get signature from delegate?
    public void Raise(params object[] _params)
    {
        lock (_delegates)
        {
            foreach (TDelegate wrappedDel in _delegates)
            {
                var del = wrappedDel as Delegate;
                del.Method.Invoke (del.Target,  _params);
            }
        }
    }
}
A: 

Please, check this out: Checking for null before event dispatching… thread safe?

Rubens Farias
+1  A: 

The latter structure will make sure you won't get a null reference exception calling the handler on non-Itanium architectures.

However, it leads to another possible issue -- it's possible for a client that registered an event handler to have the handler called after it's been removed. The only way to prevent that is to serialize raising the event and registering the handler. However, if you do that, you have a potential deadlock situation.

In short, there are three potential classes of ways this can break -- I go with the way you've done it here (and MS recommends) and accept that it is possible for an event handler to get called after it is unregistered.

Jonathan
+1  A: 

Read this post from Eric Lippert : Events and Races

Guillaume