APM – Asynchronous Programming Model

The APM always consists of a pair of methods. You call a method to enqueue work at the Threadpool. This method returns an IAsyncResult. The second method of this pair, returns the actual return value of the call. Usually those methods are prefixed with Begin__ and End__ respectively. See the following snippet with a method in synchronous and an asynchronous fashion:

Result MyMethod(int parameter);

//accompnying APM methods
IAsyncResult BeginMyMethod(int parameters, AsyncCallback callback, object state);
Result EndMyMethod(IAsyncResult iar);

Begin takes the same parameters as the synchronous version and two additionaly parameters. The EndMethod takes the IAsyncResult that was returned from the Beginmethod.

Gotchas

If the end method is called before the workitem is completed, the calling thread is blocked until completion. Also for each call of a begin method the accompanying end method must be called even though you might not be interested in the result. This is because the End method handles exceptions, and if those are not handled, you may have leaking resources.

IAsyncResult and when to call endmethod

The idea goes like this: Begin enqueus Work in thread pool and returns the control immediately to the calling code. The End method is called on completion and proceeds. The completion can be monitored with the IAsyncResult. This is done with different techniques. Which we look at in the following section.

The IAsyncResult object has the following three interesting properties to check for when to call the end method so it does not block.

  • IsCompleted,
  • AsyncWaitHandle (signals when complete, waitable object),
  • AsyncState(context in Begin methods parameter state), CompletedSynchronously(bool was operation performed on thread that called it)

1. Polling for completion

Polling is the most basic concept. You just check the IAsyncResult.IsCompleted flag periodically. If it is set to true, you can call the end method without blocking. Obviously this is not a very efficient way to go about this problem. Also code for polling can lead to messy interactions.

2.) Waiting for completion

Simply calling the end mehtod, could be a method to wait for completion. But this has no way of a timeout, for the case that your thread is deadlocked or stuck in an infinite loop. So a better way to wait for completion is to use the AsyncWaitHandle object from the IAsyncResult. WaitHandles are synchronization objects that signal at some specific circumstances. Asyncwaithandle signals when the method that returns the IAsyncResult has finished. The wait handle can be supplied with a timeout:

var asyncResult = BeginMyMethod(42, null, null);

if (asyncResult.AsyncWaitHandle.WaitOne(5000))
{
    int count;
    try 
    {
        Result result = EndMyMethod(asyncResult);
    }
    catch
    {
        // some logic to handle faults
    }
}

In general you should avoid using the AsyncWaitHandle, because when it times out it cannot call the End method and than you have leaked resources as mentioned above.

3.) Completion notification

The third way to wait for the end method is to register a completion callback as it is the most flexible. Yet it is also the most complex, especially in context of UI thread affinity. You can utilize the AsyncCallback delegate with the state object of the IAsyncResult. The callback is called when the Thread has finished its work. But beware, that the thread End method is called on a thread pool thread and not the Main Thread. This means you need a Dispatcher needed or any synchronization mechanism if you want to use this on a UI thread.

Also you need exception handling code, else the Callback error would be swallowed in the background thread.

private void OnGetResult()
{
    var iar = BeginMyMethod(42, new AsyncCallback(Callback), null);
}

private void Callback(IAsyncResult iar)
{
    try
    {
        Result result = EndMethod(iar);
    }
    catch (Exception ex)
    {
        //omitted
    }
}

APM is quite common in .Net Framework especially in I/O methods like WebRequest. Yet it comes with some caveats. One is, that The end method needs to be called on the same instance as the begin method. This could lead to problems like managing ids and references in different classes, therefore the state object in Begin can be used from IAsyncResult.AsyncState. This can be casted to the instance on which Begin was called.
It is also quite common, because delegates support APM and therefore pretty much any .Net code can be run asynchronously.

EAP – Event based async Pattern

The APM has some issues with structure of code, UI interaction, and error handling (like you need to call the end method for each begin method etc.). To make this easier, the EAP was introduced in .Net 2.0. The general idea is again that a pair of methods exists for the async operation, one method to invoke the operation and an event that called when the call completes.

private void UseEAP()
{
    var webClient = new WebClient();

    webClient.DonwloadStringCompleted += ClientOnDownloadDataCompleted;
    webClient.DownloadStringAsync(new Uri("https://t-heiten.net"));
}

private void ClientOnDownloadDataCompleted(object sender, DowloadStringCompletedEventArgs e)
{
    // omitted 
}

An important note is, that BEFORE the async operation is started, the event must be subscribed. Else there would be a race condition between the event subscruption and the completion.
The positive aspect of the EAP is, that it can be used directly on the UI Thread, beacuse the event encapsulates the use of the SynchronizationContext.

Error handling

To handle errors with the EAP you use the AsyncCompletedEventArgs.Error property. If this is set to not null, an exception was thrown during the operation. This is actually a step backwards, because it amounts to error code checking, which was surpassed by exceptions in modern languages. This makes the use of EAP a little less desireable.

Cancellation

The APIS of EAP are meant to use with a CancelAsync method. To cancel an ongoing asynchronous operation, the AsyncCompletedEventArgs.Cancelled is set to true on CancelAsync call of the invoked async method. This works as a cooperative approach only. This means you have to check in the event subscription, if the method was cancelled.

Issue of Multiple requests

An isse might arise, when you subscribe multiple instances to an async operation. THe question is, which call does the event belong to? The solution to this issue is the use of a userToken.

void DownloadStringAsync(Uri address, object userToken);

The value of token, can be found in the UserState property of the Event parameters.

Another possbile method is, to throw an exception on a second invocation (if the operation should not support a second invocation).

Summary

We looked at ways of enqueing work on the thread pool with the asynchronous programming model (APM) and the event based async pattern (EAP).

Both come with a pair of methods to describe the async operation.  Both come with their own caveats and gotchas. For APM this is mainly the messy structure the code tends to develop. The other problem is, that for every Begin method you absolutely have to call the end method to make error handling possible. This is unhelpful in the sense of code structure as well.

EAP on the other hand, has error checking for faults which is a step backwards from a programming model perspective. It also has a mandatory order in which the event has to be subscribed.

With an overview of this techniques we will progress to the TPL in the following post:

Introduction to Tasks in TPL.

Categories: TPL

0 Comments

Leave a Reply