Asynchronous .NET "Fire and Forget" Pattern
by Peter A. Bromberg, Ph.D.

Peter Bromberg

 

A little known technique that I read about quite some time ago on Mike Woodring's site just became important to me, and I though I'd share it.

Often in your short happy life as a developer you will come upon requirements to perform a certain set of actions that must be completed within a certain allotted time in order to prevent something bad (such as a TFTP packet request Timeout, for example) from happening.



I had such a requirement recently in building a TFTP server for VOIP devices. One of the requirements was to log every device request to the database. However, I have a number of complex operations going on and they must all be completed in under a second or I am "toast" - the device will think I died and that just causes a whole bunch of cascading problems that I can't afford to deal with.

Enter the Asynchronous Fire and Forget pattern! Faster than a speeding bullet, it will enable you to kick off a process or a method, never having to wait for anything, and sail on your merry way through your other stuff like a breeze. Hey, I don't have 30 milliseconds to wait for my SQL Insert call to complete! Time is just that tight. I need to Fire it off and keep galloping on!

The .NET Framework SDK docs carry a caution that mandates calling EndInvoke on delegates you've called BeginInvoke on, in order to avoid potential leaks. This means you can't just "fire-and-forget" a call to BeginInvoke without the risk of running into memory leak issues. If you need to "bone up" on delegates, I've got a piece I did sometime ago that could be helpful.

Mike's sample provided an AsyncHelper class with one public method called, appropriately, "FireAndForget", that is intended to support the fire-and-forget pattern without the fear of leaks. The usage model is that instead of calling BeginInvoke against a delegate, you would instead call the AsyncHelper.FireAndForget, passing that delegate and its parameters as input. What I've put together here is a simple example using my previously written - up SysLog Sender class that sends out greatly simplified SysLog Messages. It's probably not the greatest example, but there are some things you can't expose about your work, so it will suffice as a surrogate for something else - such as a long - running database insert, for example, to illustrate the technique.

Here's some sample code to look at and then I'll explain just below it:

using System;
using System.Reflection;
using System.Threading;
using Utils.SysLogSender;

class App
{
	delegate void SendSysLogMessageDelegate(string ipAddress,  string body);
    static void Main()
    {	
        int tid = AppDomain.GetCurrentThreadId();
        Console.WriteLine("[{0}] Main called", tid);

		SendSysLogMessageDelegate sld = new SendSysLogMessageDelegate(SendLogMessage);
		AsyncHelper.FireAndForget(sld,"127.0.0.1", "This is a Syslog test message"); 
       
        Console.WriteLine("[{0}] Press ENTER to exit", tid);
        Console.ReadLine();
    }

	static void SendLogMessage(string ipAddress, string message )
	{        
		Sender.Send(ipAddress,message);		 
		Console.WriteLine(String.Format("Sent Syslog Message to {0} with {1}",ipAddress, message));	 
	}    
}

public class AsyncHelper
{
    class TargetInfo
    {
        internal TargetInfo( Delegate d, object[] args )
        {
            Target = d;
            Args = args;
        }

        internal readonly Delegate Target;
        internal readonly object[] Args;
    }

    private static WaitCallback dynamicInvokeShim = new WaitCallback(DynamicInvokeShim);

    public static void FireAndForget( Delegate d, params object[] args )
    {
        ThreadPool.QueueUserWorkItem(dynamicInvokeShim, new TargetInfo(d, args));
    }

    static void DynamicInvokeShim( object o )
    {
        TargetInfo ti = (TargetInfo)o;
        ti.Target.DynamicInvoke(ti.Args);
    }
}

	  
	  

You'll notice the first thing you must do is declare a Delegate with a signature that exactly matches the method you need to call:

SendSysLogMessageDelegate(string ipAddress, string body);

Next, you need to create a method that serves as the target of the delegate:

static void SendLogMessage(string ipAddress, string message )
{
Sender.Send(ipAddress,message);
}

Finally, we just create an instance of the delegate, pointing to the target method, and call the FireAndForget method of the AsyncHelper class, passing in the delegate and the list of whatever parameters the target method needs. In this case, there are only two, a string IPAddress and a text message, but you can have as many as you want, because the FireAndForget method take a params object[] args parameter list:

SendSysLogMessageDelegate sld = new SendSysLogMessageDelegate(SendLogMessage);
AsyncHelper.FireAndForget(sld,"127.0.0.1", "This is a Syslog test message");

And that's the basics. No Threading issues, no memory leaks, and NO WAITING for XYZ method to return!

You can download the SysLog Sender class along with the above example below. If you aren't familiar with SysLog, I suggest you snag a copy of the free KIWI Syslog Daemon. Now you will not only be faster than a speeding bullet, you'll be able ask for a raise in a single bound!

Download the complete VS.NET 2003 solution below

 

 


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.

Article Discussion: