Preface
This article is written for Microsoft .NET 2.0 and Visual Studio 2005. For information on changes required by v.1.1 and Visual Studio 2003, please check the comments.
Asynchronous Programming Model
The Asynchronous Programming Model (APM), as implemented by delegate
s, consists of three parts:
BeginInvoke
EndInvoke
- Rendezvous techniques
BeginInvoke
starts an algorithm, implemented via a method, on a new thread.
EndInvoke
retrieves the result of that method.
The Rendezvous techniques allow you to determine when the asynchronous operation has completed.
Rendezvous Techniques
There are three different types of Rendezvous techniques you can use to retrieve the results of an asynchronous delegate
invocation. The first is Wait-Till-Completion, implemented via EndInvoke
. Calling this method will block the current thread until the results of the asynchronous method are available. This is the least effective method, as it virtually eliminates the benefits of APM.
private delegate string StringReturningDelegate();
private void Main()
{
StringReturningDelegate fd =
new StringReturningDelegate(MethodThatTakes10SecondsToComplete);
IAsyncResult receipt = fd.BeginInvoke(null, null);
string result = fd.EndInvoke(receipt);
Console.Write(result);
Console.Read();
}
private string MethodThatTakes10SecondsToComplete()
{ Thread.Sleep(10000); return "Done!"; }
This code segment demonstrates Wait-Till-Completion. You can see that it offers no benefits over calling MethodThatTakes10SecondsToComplete
synchronously, as the EndInvoke
method will block the calling thread.
The second Rendezvous technique is called Polling. In this technique, you check a property of the IAsyncResult
object is called IsCompleted
. This property will return false
until the async operation has completed. The following code segment demonstrates polling on the same method:
private delegate string StringReturningDelegate();
private void Main()
{
StringReturningDelegate fd =
new StringReturningDelegate(MethodThatTakes10SecondsToComplete);
IAsyncResult receipt = fd.BeginInvoke(null, null);
Console.Write("Working");
while (!receipt.IsCompleted)
{
Thread.Sleep(500);
Console.Write('.');
}
string result = fd.EndInvoke(receipt);
Console.Write(result);
Console.Read();
}
private string MethodThatTakes10SecondsToComplete()
{ Thread.Sleep(10000); return "Done!"; }
This method isn't much better. You sleep away all the extra time that you could have been productive with (like in college). If you wanted to, you could take advantage of the loop to show some kind of procedural animation to the user, thus keeping them informed and aware that your program hasn't locked up. This makes this technique a little more useful than Wait-Till-Completion.
The third, and most efficient, Rendezvous technique is Method Callback. In this technique, you pass a delegate
to the BeginInvoke
method that will be called when the asynchronous operation has completed. It will not block your execution, or waste any CPU cycles. You should always use this method of Rendezvous.
private delegate string StringReturningDelegate();
private void Main()
{
StringReturningDelegate fd =
new StringReturningDelegate(MethodThatTakes10SecondsToComplete);
fd.BeginInvoke(AsyncOpComplete, null);
Console.Read();
}
private void AsyncOpComplete(IAsyncResult receipt)
{
AsyncResult result = (AsyncResult)receipt;
StringReturningDelegate gsld = (StringReturningDelegate)result.AsyncDelegate;
string result = gsld.EndInvoke(receipt);
Console.Write(result);
}
private string MethodThatTakes10SecondsToComplete()
{ Thread.Sleep(10000); return "Done!"; }
This method allows the program to continue execution while the async operation completes on another thread. When the operation completes, the delegate will call the AsyncOpComplete
method, which was passed to the delegate via the BeginInvoke
method.
No Changes Required
Take note that the implementation of MethodThatTakes10SecondsToComplete
has not changed. This is the major strength of the APM. You create the method you wish to call asynchronously just as you would if it were to be used synchronously. All the work required to call this method asynchronously is performed by the delegate
. All you have to do is create a delegate
that matches the signature of your method, and construct a method (that returns void
and takes one IAsyncResult
parameter) designed to be run upon completion of the async operation.
Limitations of the APM in .NET
APM via delegate
s is an extremely useful and agile tool that you can use to make your programs run faster and be more responsive, but with power comes responsibility. Improper usage may leak resources. For every BeginInvoke
call, you must call EndInvoke
to prevent this. Additionally, you must understand that asynchronous operations can be less efficient than fast, synchronous operations. Save them for I/O bound operations or compute bound operations that you know will take time.