Windows Forms Eventing: Thread Synchronization

So last time, we created this great event aggregator for our Windows Forms applications.Instead of having the code that sends messages directly connected to the code thatreceives messages, everybody just knows about the event aggregator. This works thesame way you don’t need turn-by-turn directions to Fox News Headquarters to mail thema box of dirty diapers.

The catch is that the code so far only works if all the senders and receivers areon the same thread. If they are on different threads, who knows what may happen? (Notme. I failed out of Home-Ec. I don’t even know how you turn cotton flowers into threads,let alone how to make them fit together.)

There is a simple fix, however. The .NET 2.0 SynchronizationContext is used by WindowsForms and can provide an easy-to-use central choke point to manage all of our thread-to-threadcommunications.

Remember Your Singleton

When I was cutting and pasting together this code from the Intarnets, one big problemI had was forgetting to initialize my singleton correctly. It’s really importantthat you initialize your singleton EventAggregator from SynchronizationContext.Currentin the Windows Forms Thread. Setting the thing up right in StructureMap oryour IOC Container of choice works just great. Just make sure it gets done. (My mistakealso involved letting StructureMap use “new SynchronizationContext()” in initializingmy singleton rather than “SynchronizationContext.Current”.) Get it right the firsttime and you won’t have to do multithread debugging.

EventAggregator Class

public class EventAggregator: IEventAggregator{private readonly SynchronizationContext_context;private readonly List<object>_listeners = new List<object>();private readonly object _locker= new object();public EventAggregator(SynchronizationContextcontext){_context = context;}#region IEventAggregator Memberspublic void SendMessage<T>(Tmessage){sendAction(() => all().CallOnEach<IListener<T>>(x => { x.Handle(message);}));}public void AddListener(object listener){withinLock(() =>{if (_listeners.Contains(listener)) return;_listeners.Add(listener);});}public void RemoveListener(object listener){withinLock(() => _listeners.Remove(listener));}#endregionprivate object[]all(){lock (_locker){return _listeners.ToArray();}}private void withinLock(Actionaction){lock (_locker){action();}}protected virtual void sendAction(Actionaction){_context.Send(state => { action(); }, null);}}

Notice the locking. Notice the creation of a copy of the _listeners list into an arrayfor Thread Safety. Most importantly, notice the use of _context.Send(). A few minorchanges…but now the whole class is thread safe, and synchronous between threads. Hooray!

Next Time

I’ve still got lots to cover on this subject. How do you use the eventing in yourapplication architecture. Using Weak References to protect your event system frommemory leaks. Sending events to a specific target or set of targets. I’m going tobe working on some other projects for a bit, so it may be a while before I write thoseposts, but hopefully what we’ve covered so far is useful on its own until then.

Resources