Click here to Skip to main content
15,867,594 members
Articles / Operating Systems / Windows

The Power of the Asynchronous Programming Model as Implemented by Delegates

Rate me:
Please Sign up or sign in to vote.
3.54/5 (20 votes)
21 Jun 2006CPOL3 min read 88.7K   47   23
The Asynchronous Programming Model (APM) is implemented by Delegates, allowing you to easily invoke any method asynchronously

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 delegates, consists of three parts:

  1. BeginInvoke
  2. EndInvoke
  3. 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.

C#
// a delegate for a method that takes no params and returns a string 
private delegate string StringReturningDelegate();
private void Main()
{
    // create an instance of the delegate pointing to a method 
    // that takes ten seconds to complete     
    StringReturningDelegate fd = 
        new StringReturningDelegate(MethodThatTakes10SecondsToComplete);
    // Begin invocation of this delegate     
    IAsyncResult receipt = fd.BeginInvoke(null, null);
    // Immediately call EndInvoke, which will block for, oh, say, 
    // right about ten seconds     
    string result = fd.EndInvoke(receipt);
    Console.Write(result);
    Console.Read();
}
// A method that takes 10 seconds, then returns a string 
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:

C#
// a delegate for a method that takes no params and returns a string
private delegate string StringReturningDelegate();
private void Main()
{
    // create an instance of the delegate pointing to a method 
    // that takes ten seconds to complete    
    StringReturningDelegate fd = 
        new StringReturningDelegate(MethodThatTakes10SecondsToComplete);
    // Begin invocation of this delegate                
    IAsyncResult receipt = fd.BeginInvoke(null, null);
    Console.Write("Working");
    // Poll IsCompleted until it returns true; 
    // Sleep the current thread between checks to reduce CPU usage    
    while (!receipt.IsCompleted)
    {
        Thread.Sleep(500);  // wait half a sec        
        Console.Write('.');
    }
    string result = fd.EndInvoke(receipt);
    Console.Write(result);
    Console.Read();
}
// A method that takes 10 seconds, then returns a string
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.

C#
// a delegate for a method that takes no params and returns a string
private delegate string StringReturningDelegate();  
private void Main()        
{                 
    // create an instance of the delegate pointing to a method 
    // that takes ten seconds to complete            
    StringReturningDelegate fd = 
        new StringReturningDelegate(MethodThatTakes10SecondsToComplete);     
    // Begin invocation of this delegate                 
    fd.BeginInvoke(AsyncOpComplete, null);    
    // Do tons of work here.  No, seriously.
    Console.Read();        
}
/// <summary>
/// Retrieves the results of MethodThatTakes10SecondsToComplete 
/// when called asynchronously
/// </summary>
/// <param name="receipt">The IAsyncResult receipt.</param>
private void AsyncOpComplete(IAsyncResult receipt)
{
    //  Cast to the actual object so that we can access the delegate
    AsyncResult result = (AsyncResult)receipt;
    //  retrieve the calling delegate
    StringReturningDelegate gsld = (StringReturningDelegate)result.AsyncDelegate;
    //  Retrieve our results; this is guaranteed not to block, 
    //  as the async op is complete
    string result = gsld.EndInvoke(receipt);
    //  write the result to the console
    Console.Write(result);
}
// A method that takes 10 seconds, then returns a string        
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 delegates 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
Will Sullivan is a full time C# programmer in Columbia, SC. He enjoys sticking his finger through his right nostril and out his left tear duct. In photoshop.

Comments and Discussions

 
GeneralMy vote of 5 Pin
stef.tsal21-Nov-11 17:55
stef.tsal21-Nov-11 17:55 
GeneralWorker Thread GUI updates [modified] Pin
Bob Sandberg26-Jun-06 5:03
Bob Sandberg26-Jun-06 5:03 
GeneralRe: Worker Thread GUI updates Pin
William Sullivan26-Jun-06 10:00
William Sullivan26-Jun-06 10:00 
QuestionAsync operation comparison question? Pin
SDragon4221-Jun-06 5:42
SDragon4221-Jun-06 5:42 
AnswerRe: Async operation comparison question? [modified] Pin
o_manolis22-Jun-06 23:30
o_manolis22-Jun-06 23:30 
GeneralRe: Async operation comparison question? Pin
William Sullivan26-Jun-06 4:27
William Sullivan26-Jun-06 4:27 
GeneralMethod Call Back Example Problem [modified] Pin
Mike DiRenzo19-Jun-06 9:21
Mike DiRenzo19-Jun-06 9:21 
GeneralRe: Method Call Back Example Problem Pin
Petru6619-Jun-06 20:58
Petru6619-Jun-06 20:58 
GeneralRe: Method Call Back Example Problem Pin
William Sullivan26-Jun-06 4:28
William Sullivan26-Jun-06 4:28 
GeneralUm... Well... Pin
Ken Hadden19-Jun-06 8:10
Ken Hadden19-Jun-06 8:10 
GeneralRe: Um... Well... Pin
William Sullivan20-Jun-06 11:30
William Sullivan20-Jun-06 11:30 
GeneralRe: Um... Well... Pin
Chris Meech21-Jun-06 3:18
Chris Meech21-Jun-06 3:18 
QuestionWorker Threads and Window Messages Pin
Chris Meech19-Jun-06 7:37
Chris Meech19-Jun-06 7:37 
AnswerRe: Worker Threads and Window Messages Pin
William Sullivan20-Jun-06 11:38
William Sullivan20-Jun-06 11:38 
GeneralRe: Worker Threads and Window Messages Pin
Chris Meech21-Jun-06 3:17
Chris Meech21-Jun-06 3:17 
GeneralDo not agree... Pin
Edgardo Menta19-Jun-06 7:00
Edgardo Menta19-Jun-06 7:00 
GeneralRe: Do not agree... [modified] Pin
William Sullivan20-Jun-06 11:50
William Sullivan20-Jun-06 11:50 
Edgardo Menta wrote:
As far as i know, there is only two methods:
- EndInvoke
- Callback


As far as I know, I haves no idea what you mean by "there is only two methods." If you are talking about methods of rendezvous, you are mistaken. As the example showed, you can use the IsCompleted property of the IAsyncResult interface to test if the async operation has completed. You can spinlock on this property until it returns true. That is, if you hate everybody who is running on the current machine.


Edgardo Menta wrote:
But of course if you use EndInvoke the way you did it, without doing anything else between BeginInvoke and EndInvoke, it has no sense.



Obviously, the examples use the least amount of code to demonstrate the point. I suppose I could have had put the code in a sample application called, oh, I don't know... Nitpick Generator, where after I invoked the delegate I spunlocked on a method that generated random nitpicky things which then printed to the screen until the callback method was run, which at that point would null out the nitpick generator and garbage-collect it... but I'm lazy. I apologize.

Edgardo Menta wrote:
And of course, "you should always use callback", is not true.


I could say, "You should never kick somebody in the jellies," which everybody would agree is true; however there are some times when you just have to do it. Just as in there are some times when you just have to call EndInvoke when its possible your calling thread will be blocked. However, if it doesn't matter that your calling thread (be it UI or other) be blocked, then why not just go with a synchronous operation? Async ops, tho they take advantage of multiprocessor machines, do cost in terms of time (to construct) and resources (additional memory pressure).
(modified)
I guess I was drowning in my own snarkiness to remember that you HAVE TO call EndInvoke for every BeginInvoke, otherwise you leak resources. The sole exception is Control.BeginInvoke, which will not leak resources if you don't call EndInvoke.

-- modified at 8:47 Monday 3rd July, 2006
GeneralRe: Do not agree... Pin
Edgardo Menta20-Jun-06 15:18
Edgardo Menta20-Jun-06 15:18 
GeneralRe: Do not agree... Pin
William Sullivan21-Jun-06 2:46
William Sullivan21-Jun-06 2:46 
GeneralParameter to BeginInvoke... Pin
Petru6618-Jun-06 23:21
Petru6618-Jun-06 23:21 
GeneralRe: Parameter to BeginInvoke... Pin
William Sullivan20-Jun-06 11:54
William Sullivan20-Jun-06 11:54 
GeneralRe: Parameter to BeginInvoke... Pin
Petru6620-Jun-06 21:54
Petru6620-Jun-06 21:54 
GeneralRe: Parameter to BeginInvoke... Pin
William Sullivan21-Jun-06 2:48
William Sullivan21-Jun-06 2:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.