Publish Subscribe Pattern in C# and some gotchas

The eventing mechanism built into the .NET runtime makes it a piece of cake to implement a simple publisher – subscriber pattern (C# code follows).

public class EmployerPublisher
{
   public event  PayrollArrivedHandler PayrollArrived;
   public void FireEvent(DateTime today)
   {
	if(PayrollArrived != null)
	  PayrollArrived(today); 
   } 
// Subscriber class - Employee
public class Employee
{
	public void OnPayrollArrival(DateTime day)
       { 
		// Round up from friends and go spend the money!
       }  
} 
// Hooking up the publisher and subscriber(s) in the client code
 
EmpoyerPublisher publisher = new EmployerPublisher();
Employee employee1 = new Employee();
Employee employee2 = new Employee();
publisher.PayrollArrived += employee1.OnPayrollArrival;
publisher.PayrollArrived += employee2.OnPayrollArrival;
// Now that the subscribers are hooked up, the event can be fired
DateTime now = DateTime.Now;
publisher.FireEvent(now.date);

However – there are a couple of gotchas to watch out for:

  1. When multiple subscribers are present – how do you ensure that the publisher does not get blocked – i.e. – will each subscriber be well-behaved enough to only perform short tasks?
  2. What if a subscriber is added while notification is in progress – i.e. – a list of subscribers is being notified – and a new subscriber happens to come along (this is not a rare event – especially if any one of the subscribers happens to contain time-consuming code – i.e. is badly behaved.

Let us look at these one by one:

Problem 1 – The badly-behaved (subscriber) problem

The well-behaved problem is best solved by working under the assumption that all subscribers will be badly behaved! So what is a publisher to do? One solution is to launch each notification on a new thread – that way even if a subscriber is badly behaved – it does not tie down the notification of other subscribers. Since the CLR in .NET provides a ready-made thread-pool, this is a seemingly simple solution. In fact, all one needs to do is use the built in support for asynchronous delegates (the event defined in the publisher class is really just a delegate). How would this look? 

public class EmployerPublisher

{
   public event  PayrollArrivedHandler PayrollArrived
   // This event firing is synchronous - i.e. the publisher instance is tied down until the subscriber finishes processing OnPayrollArrival
public void FireEvent(DateTime today)
   {
	if(PayrollArrived != null)
	  PayrollArrived(today); 
   } 
// This is an asynchronous version of the same event-firing - just fire it - and do not worry about how long the subscriber takes - since subscriber processing 
// is on a different thread.
   public void FireEventAsynchronous(DateTime today)
   {
	if(PayrollArrived != null)
	  PayrollArrived.BeginInvoke(today, null, null); 
   } 

We are almost there. The only problem with the above code is a c# restriction – if you want to call BeginInvoke, your delegate (our event) can only have one target.


We already have two targets (employee1.OnPayrollArrival and employee2.OnPayrollArrival) - so what do we do? How about if we take our delegate and look at all its 
targets one by one - and call BeginInvoke on them one at a time? That should work around the original problem.
public class EmployerPublisher
{
   public event  PayrollArrivedHandler PayrollArrived
   // This event firing is synchronous - i.e. the publisher instance is tied down until the subscriber finishes processing OnPayrollArrival
public void FireEvent(DateTime today)
   {
	if(PayrollArrived != null)
	  PayrollArrived(today); 
   } 
// This is an asynchronous version of the same event-firing - just fire it - and do not worry about how long the subscriber takes - since subscriber processing is on a different thread.
   public void FireEventAsynchronous(DateTime today)
   {
	if(PayrollArrived != null)
      {
	 Delegate[] delegates = PayrollArrived.GetInvocationList();
         foreach(Delegate del in delegates) 
         {
		PayrollArrivedHandler handler = (PayrollArrivedHandler) del;
               handler.BeginInvoke(today, null, null) 
         } 
   } 
}    
Problem 1 (the badly-behaved subscriber problem) is essentially taken care of - by letting the subscribers stay badly behaved - we just don't care - we've still managed to notify all the subscribers successfully. 
Problem 2 : A subscriber is added while notification is in progress
This is a slightly trickier problem to solve. We still want to allow the new subscriber to add itself - but we do not want to interfere with the current notification process.
 This solution is borrowed from Holub on Patterns - and works well in our scenario as well. The basic idea is that instead of having just ANY collection of subscribers - use a linked list to store subscribers.
Notification of the list of subscribers boils down to traversing the linked lists one node at a time. Say you are on some intermediate node - and a new subscriber arrives. Simply add the new subscriber (as a node) to the head of the list!
The head of the list is not affected by where the traversal’s current pointer is. This way - it doesn't interfere with the existing notification process - and is successfully added to the list of subscribers whenever the next notification comes around. 

Specializing in high volume web and cloud application architecture, Anuj Varma’s customer base includes Fortune 100 companies (dell.com, British Petroleum, Schlumberger).

For Anuj’s popular, one-on-one executive tech seminars, visit ANUJ.COM

All content on this site is original and owned by AdverSite Web Holdings, Inc. – the parent company of anujvarma.com. No part of it may be reproduced without EXPLICIT consent from the owner of the content.

Anuj Varma – who has written posts on Anuj Varma, Technology Architect.


Leave a Reply

Your email address will not be published. Required fields are marked *