Asynchronous fire-and-forget method calls in .NET

Much like distributed architectures, the first law of asychronous programming is don't do it (or you do not talk about asychronous programming). However, sometimes you don't have a choice. If I absolutely must write asynchronous code I prefer to use the fire-and-forget model because I find that it minimises threading issues.

The application that I am currently working on receives data on a TCP socket. The data comes in frequent small amounts and requires a significant amount of time to process (~ 1s). As a single threaded service the socket queue is quickly filled with clients waiting to transmit their data. What I wanted was a TCP server that could listen for incoming data, accept the data, and then pass the data to another thread for processing. Because the server does not care about the result of the processing this is a fire-and-forget model.

To implement this style of asynchronous programming in .NET I started by coding the method that I want to run asynchronously:

private void Log(string received)
{
    // do stuff           
}

The next step is to define a delegate that matches the asychronous method:

delegate void LogAString(string received);

Delegates provide a method called BeginInvoke that allows us to call the delegate asychronously. This method call does not block which means that my TCP server can immediately continue processing requests. According to the documentation it is important to always call EndInvoke when the asychronous task completes. To do this we need to define a callback method that calls EndInvoke on the delegate:

private void DoneLogging(IAsyncResult result)
{
    var dlgt = (LogAString)result.AsyncState;
    try 
    {
        dlgt.EndInvoke(result);
    }
    catch (Exception ex)
    {
        // handle any exceptions from the other thread 
        // (thanks to Vyacheslav Trenogin for this)
    }
}

The final step is to invoke the delegate when the TCP server receives some data:

var logDelegate = new LogAString(Log);
logDelegate.BeginInvoke(receivedFromSocket, DoneLogging, logDelegate);

Comments

Comments are closed