+2  A: 

There is no need for the private ProcessCompleted member to be an event - it could just be a field: private EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - inside the class it always goes straight to the field, so the event stuff is lost anyway.

The approach you've shown with an explicit lock object isn't much more thread-safe than just having a field-like event (i.e. public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - the only difference is that you aren't locking "this" (which is a good thing - you should ideally avoid locking on this).. The "handler variable" approach is the right one, but there are still side-effects you should be aware of.

Marc Gravell
Added why I used the private eventhandler and the explicit event stuff to my question. Is it still not needed? And what do you mean by that difference? Does a public event EventHandler<ProcessCompletedEventArgs> SomeEvent lock on this automatically?
Svish
Yes; field-like events (i.e. an event without an explicit add/remove) has an inbuilt lock(this); see 10.8.1 in the language spec (MS version); however, this is bypassed by code inside the class - see http://marcgravell.blogspot.com/2009/02/fun-with-field-like-events.html; so as a *private* event, the add/remove (and thus lock) is never used. For an explicit interface implementation, the code is fine, and you'd need to add the lock yourself, which you have done - and arguably *better* than a "lock(this)". Stick with it ;-p
Marc Gravell
alrighty =)
Svish
+3  A: 

You need to lock when you fetch the handler too, otherwise you may not have the latest value:

protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
    EventHandler<ProcessCompletedEventArgs> handler;
    lock (completedEventLock) 
    {
        handler = ProcessCompleted;
    }
    if (handler != null)
        handler(this, e);
}

Note that this doesn't prevent a race condition where we've decided we're going to execute a set of handlers and then one handler unsubscribed. It will still be called, because we've fetched the multicast delegate containing it into the handler variable.

There's not a lot you can do about this, other than making the handler itself aware that it shouldn't be called any more.

It's arguably better to just not try to make the events thread-safe - specify that the subscription should only change in the thread which will raise the event.

Jon Skeet
Are you sure, the lock is necessary? The delegates are immutable and assignmend is atomic operation, so no lock is necesary I think.
TcKs
See my comments to your post. You absolutely need locking to make it thread-safe.
Jon Skeet
TcKs
Without the lock, there's no memory barrier so there's no guarantee that you'll see the latest value. See http://pobox.com/~skeet/csharp/threads/volatility.shtml
Jon Skeet
Even with the lock there is no guarantee that you see the latest value.That's because it depends who comes first:the changer or the caller.So you only need the locks around adding/removing because both are two steps in memory: reading and writing.So between reading and writing another changer can read and write so changer 1 overwrites the changes of changer 2.But this does not disturb the caller.It may read between the changer's reading and writing but I don't see any problem with that.(You cannot tell which value the caller gets if both run more or less at the same time; with or without lock)
rstevens