I'm trying to figure out how to safely use .NET events/delegates in a
thread-safe class. There are a couple problems. One is that the standard
"if(EventName != null) EventName(...);" call can fail if the event is
emptied of all methods between the two statements, implying that some sort
of synchronization between this and removals from EventName is needed. The
other problem is that if an event with a set of delegates is in the process
of being called, and you remove a method from the set, that method might
still get called one last time _after_ you removed it, as you can't modify
the executing delegate (they're immutable, so the removal creates a new one
with that method removed). That creates a bit of a problem if I've disposed
an object, it's removed itself from an event hook, then later gets called
via the removed event hook again. Ick.
A partial solution I thought of is to try synchronizing access to the event
calling method and removals from the event delegate. It's only a partial
solution, as methods called by the event could cause a removal from the
event (because they're called from within the synchronization), and the
latter problem would be exhibited again.
There must be a Right Way to handle this, as I'm sure folks have written
thread-safe classes that use .NET events/delegates. I just can't find any
examples or documentation about this. Can anyone help? Is there a design
pattern / standard for this that doesn't involve screwing up the semantics
of .NET events?
I've put up two test cases for the problems I mentioned here:
http://www-cse.ucsd.edu/~sbrown/oddities/ . I've also included a more
formal description of the problems and an example class that would exhibit
them below (also at that link):
using System;
using System.Threading;
// This is a basic example of trying to use .NET events in thread-safe
classes.
// There are a couple problems in this class:
//
// 1. There is a race between checking SomeEvent != null and calling
SomeEvent.
// If all methods are removed from SomeEvent between those points, an
exception
// is thrown.
//
// 2. There is a race when calling SomeEvent and removing a method from
// SomeEvent. Since delegates are immutable, removing a method from the
event
// during a call of the delegate will not stop that method from being
called;
// it could still get called one last time even after being removed if it
// hadn't yet been called by the executing delegate.
//
//
// Problem #1 is common to any class that claims to be thread-safe and uses
// events.
//
// Problem #2 seems common to the .NET framework and as such probably must
be
// handled by the receiver somehow.
//
// I've yet to see examples of how either problem is supposed to be
addressed.
// I desperately need said examples.
//
//
// Syncrhonization could be used between OnSomeEvent and modifications to
// SomeEvent to partially address the problems, but problem #2 would still
// exist for methods called by SomeEvent, since they would be called from
// within the syncrhonization. It feels like the receiver must be the one
to
// address problem #2, not the sender.
public class SomethingThatCreatesEvents : IDisposable {
private Thread thread;
public SomethingThatCreatesEvents() {
thread = new Thread(new ThreadStart(EventDispatcher));
thread.Start();
}
private void EventDispatcher() {
while(!disposed) {
OnSomeEvent();
}
}
public event EventHandler SomeEvent;
protected virtual void OnSomeEvent() {
if(SomeEvent != null) {
SomeEvent(this, new EventArgs());
}
}
private bool disposed = false;
public void Dispose() {
if(!disposed) {
disposed = true;
thread.Join();
}
}
}