Developer

Using asynchronous method calls in C#

Your Web site customers are immediately turned off by poor performance and refuse to wait. The .NET platform addresses this issue with asynchronous communication support. This article demonstrates how to get started with C#.


Several classes in the .NET Framework Base Class Library (BCL) provide both synchronous and asynchronous method signatures. Because a synchronous method call can create a delay in program flow, an asynchronous approach may be better in certain situations, such as when a program needs to send out requests to multiple Web services. This article will show you how to use asynchronous method calls for .NET development with C#.

Synchronous vs. asynchronous
A synchronous method call waits for the method to complete before continuing with program flow, whereas an asynchronous method call will return immediately so that the program can perform other operations while the called method completes its work.

To illustrate the advantages of using an asynchronous method call, let's look first at an example where a synchronous solution may not be the best choice. We'll demonstrate with a look at the System.Net.Dns class.

The synchronous version of the Resolve method has the following signature:
public static IPHostEntry Resolve(
string hostName
);

The IPHostEntry method accepts a single argument, the hostName, which can be either a DNS name (www.mydomain.com) or an IP address in dotted-quad notation (such as 10.10.14.2). It returns an IPHostEntry object. The IPHostEntry object members may be used to retrieve the host name, IP addresses, and other information related to this particular host.

You could use the Resolve method synchronously in your program with code such as the following:
IPHostEntry host = Dns.Resolve(“10.10.14.2”);
Console.WriteLine(host.HostName);

The problem here is that as soon as Dns.Resolve is called, your program will block execution until the Resolve method completes its work and returns an IPHostEntry object, which may take several seconds. Since DNS resolution involves network access, this method call can potentially be affected by factors such as network latency and other issues that can delay operation and is thus well suited to the asynchronous approach.

Asynchronous design
Asynchronous communication between components requires a design pattern so that the program can determine when or if a called method has completed executing. This design pattern allows the program to obtain any results returned by the method. This capability is especially useful when using Web services because your program can send requests to multiple Web services at once and wait until each returns results before continuing with program execution.

In this scenario, the Web services used may be provided on networks with different degrees of latency and by various companies across the Internet. Consequently, the length of time required to complete a request is affected by several factors that are beyond your program’s control.

The asynchronous design pattern used in the .NET Framework is characterized by the use of BeginXXXX and EndXXXX methods, where XXXX is the name of the synchronous version of the method. Let's take a closer look at the asynchronous approach.

The BeginResolve method and IAsyncResult
To avoid blocking your program’s execution (as in the first example), you can opt to use the asynchronous version of the Resolve method: BeginResolve. This method has the following signature:
public static IAsyncResult BeginResolve(
string hostName,
AsyncCallback requestCallback,
object stateObject
);

The BeginResolve method accepts the same parameter as the synchronous version, hostName, but adds two more parameters that are part of the asynchronous design pattern: the requestCallback and stateObject parameters. We will discuss these shortly, but first I’d like to call your attention to the return value, an IAsyncResult interface.

When you call a method asynchronously, the call immediately returns to your program, before the method being called has a chance to finish (or sometimes even start). By definition, there is no IPHostEntry object for the BeginResolve method to return, so instead it returns a waitable object, the IAsyncResult interface, which you can use later to retrieve the results of the call. The IAsyncResult interface is defined as follows:
public interface IAsyncResult {
object AsyncState {get;}
WaitHandle AsyncWaitHandle {get;}
bool CompletedSynchronously {get;}
bool IsCompleted {get;}
}

The first property in this interface, AsyncState, will return the same object that was passed into the BeginResolve method's stateObject parameter. There are no restrictions on this argument value. It can be anything the program wants to use to track this particular method call. It is not used or manipulated by the called method in any way.

The second property in the IAsyncResult interface, the AsyncWaitHandle, can be used by your program to wait on the method to complete by passing it to one of the WaitHandle class’ methods, WaitAll, WaitOne, or WaitAny. This is useful if you are sending several asynchronous method calls to operate in parallel and would like to make sure they all complete before continuing your own program’s work, especially if your program’s continued operation is dependent upon the results of one or more of these calls.

The third property in the interface, CompletedSynchronously, returns a Boolean value indicating whether the method was able to be completed by the time the BeginResolve method returned.

The fourth property, IsCompleted, returns a Boolean value indicating whether the work being done by the method has completed. This is useful if you use a polling mechanism to determine whether the asynchronous call has completed.

Actually, there are four ways to make and complete .NET asynchronous calls. You can use polling (the IsCompleted property) or a callback delegate (discussed below), you can use the AsyncWaitHandle to wait on the call to complete, or can call the EndXXXX method yourself with the IAsyncResult returned from the BeginXXXX method and have it wait on the call to complete. The difference between these last two techniques is that if you perform the wait yourself (using the AsyncWaitHandle), you can “wake up” every now and then based on a timeout and decide at that point whether you really want to wait any longer.

EndResolve method and AsyncCallback delegate
One of the ways to complete an asynchronous method call is to supply an AsyncCallback delegate to the BeginXXXX method. This delegate has the following signature:
public delegate void AsyncCallback(
       IAsyncResult ar
);

By providing a method in your program with this signature and creating an AsyncCallback delegate pointing to that method, you can have the asynchronous method call notify your program when it has finished processing. The following code snippet shows how you would call the Dns class BeginResolve method using a callback:
AsyncCallback callback = new AsyncCallback(GetResult);
IAsyncResult ar = Dns.BeginResolve("10.10.14.2", callback, null);
// Do some other work while the above finishes.

When the BeginResolve method finishes executing and has an IPHostEntry object to return, it calls the delegate passed to it allowing us to obtain the result. The following code shows how the GetResult method in our program could be implemented:
private void GetResult(IAsyncResult ar) {
IPHostEntry host = Dns.EndResolve(ar);
}

The Dns class calls the GetResult method when finished resolving the requested IP address, passing the same IAsyncResult interface that was returned from the initial call to BeginResolve. This allows us to call EndResolve using that argument to obtain the result of the call.

Summing up
Although you can use either synchronous or asynchronous method calls in .NET, there are times when an asynchronous approach is more efficient. This article has shown how to use asynchronous method calls with C#. In a future article, I will show you how to use the asynchronous design pattern to create your own asynchronous methods for long-running operations.

Editor's Picks

Free Newsletters, In your Inbox