The Power of the Asynchronous Programming Model as Implemented by Delegates

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:

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.

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:

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.

// a delegate for a method that takes no params and returns a string
privatedelegatestring StringReturningDelegate();
privatevoid 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>///<paramname="receipt">The IAsyncResult receipt.</param>privatevoid 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
privatestring 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.

Comments and Discussions

One very interesting aspect of invoke usage is that of GUI updates, i.e. how you actually update your GUI from a worker thread or invoked delegate.

The problem existed as well in MFC coding. A worker thread can not simply update a GUI element. It could possibly clash with the main GUI thread. In MFC, instead you would post or send a message from the worker thread to the main GUI thread. The GUI thread would then process this message to update the GUI.

In .NET, I don't fully understand how it works, but I think when you have a WinForm based class object, the invoke will run in the thread of the main GUI, so that it can update the GUI element with thread safety. So if you have a worker thread process, say one that is FTPing a large file, and you want a progress bar in your GUI, then the worker thread would periodically invoke a delegate of the progress bar window with a percent complete parameter. The invoked delegate would run in the progressBar window's thread and the GUI update would be made safely.

I'd like to see a response that expands on this topic and explains it with some good examples.

Invoke and InvokeRequired are methods inherited from System.Windows.Forms.Control. That means that you can use them on any Windows Forms control, from a lowly Label all the way up to a Form.

For performance reasons, MS decided not to make instances of controls thread safe (in fact, practically no object instances in the framework are thread safe; all static objects and methods are, however). Controls also need access to the message pump (started by Application.Run()) in order to function properly. So the initial thread that executes when your program runs (check the Main function in program.cs (.NET 2.0)), and that creates the initial form and starts the message pump, is thus referred to as the "UI Thread". It is associated with the message pump and any forms and other controls it creates.

Controls know what thread they were created on, and check to make sure that any calls that manipulate their state are run by this thread. Any attempt to manipulate that state from a thread other than the UI thread will cause the control to throw an InvalidOperationException. To allow for this cross-thread modification of the UI, the Control class offers the InvokeRequired property and the Invoke (and InvokeBegin etc.) method.

When you check InvokeRequired, the control (Form or otherwise) retrieves the window thread process ID of its parent control (this propagates up to the initial parent, I believe). It then compares this ID with the ID of the calling thread. If they do not match, this method returns true (it may return false when the parent control's handle doesn't exist, but that's a rare occurrence that is covered in the documentation). This tells you that the current thread cannot safely manipulate the control. The solution to this problem is to send a message to the UI thread telling it to execute a method for you. This is what Invoke (and its sibilings) does for you.

The Invoke methods essentially (looking at the method in Reflector leads me to keep it simple!) posts a message to the UI thread's message pump, asking it to execute the method passed to Invoke via a delegate (function pointer). When this message is read by the UI thread's message pump, the UI thread executes the method pointed to by the passed delegate, which will be run by the UI thread and is therefore free to modify the Control. Calling Invoke will block the calling thread until the UI thread has completed its task. Calling BeginInvoke will return immediately so that the calling thread can continue processing while the UI thread services the request asynchronously (not calling EndInvoke will not leak resources in this sole case, so it is safe to ignore this part of the APM).

An interesting note--calling Invoke synchronously can cause a bottleneck that will result in your program becoming nonresponsive, if you're not careful. The following code snippet will demonstrate this:

To use this code, create a standard WinForm project in C#, add a sigle RichTextBox to the form and call it 'rtb', and then paste this into the codebehind for the form. Since I know that Foo is only called from worker threads, I don't bother to check InvokeRequired. Notice that I call Handle to explicitly cause the form to create its window handle, without which Invoke will not work (this is usually not needed; the fact that I start the worker threads in the constructor necessitates this).

This code demonstrates that worker threads pounding the message pump has the same effect as running the code synchronously via the UI thread. The message pump becomes clogged by calls to run UpdateDisplay, and the worker threads get blocked while waiting for their Invokes to be serviced. In a situation like this, you would want to add Thread.Sleep to the worker threads, indicating a small amount (like 10ms) to give the CPU time to block the worker thread and allow the UI thread to process some of its messages.

I have a question though, since this article was targeted at the 2.0 framework. How does the Delegate APM compare to using the BackgroundWorker component in the System.ComponentModel namespace? Specifically in regards to performance and expense to create.

I've been using the BackgroundWorker without any problems for a very short while. It seems that it handled most of the complexities async operations internally.

I think this control pretty much yields the same result as using the 'Callback Delegate APM'.

By my understanding, both of them, in the end, use the ThreadPool to queue your method for execution. Checking with Reflector, I can see that, in fact, the BackgroundWorker in fact uses BeginInvoke on the delegate you pass to it. The benefit of the BackgroundWorker is that it wraps the whole thing up in a nice package, and provides for a means of communication between the working thread and the calling thread. That's always nice. I'd say definitely stick with the BW if you're comfortable with it.

How is the third method use you describe, of providing a delegate function to be called at the end of the thread, any different from having a thread post a registered windows message and then exit/terminate and have the originating thread handle the registered message?

Additionally there are occassions where it will make sense to effectively 'single thread' a collection of worker threads in order to guarantee dependancies that may exist. And so I've seen code like this

The difference is that by posting the message you force the UI thread to service the async operation. That negates one of the benefits of asynch ops -- keeping your UI thread responsive to the user. If you need to do more processing prior to updating the UI, you may cause the app to show that lovely (not responding) message that users love oh so much.

I'm not saying that you should use async delegate invocation wherever possible. You have to program for the particular situation. Most of the time synchronous programming is more than sufficient; from there, ThreadPool.QueueUserWorkItem is fine, then comes async delegate invocation, and lastly instances of System.Threading.Thread. Use the tools that work best for your situation.

And both have different uses. But of course if you use EndInvoke the way you did it, without doing anything else between BeginInvoke and EndInvoke, it has no sense. But you could start a thread, continue doing something else and when ready use the EndInvoke to be sure the other thread has completed its task. And of course, given the threads do not compete over the same resources. If they do, there is no benefit on multithreading, except user interface response.

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.

I did´nt mean your are lazy. I mean, that your sample shows what u said: EndInvoke is no useful, because it blocks your thread. But only because you do nothing between BeginInvoke and EndInvoke. But false premises, so false conclusions turns true. You could do something else between BeginInvoke and EndInvoke, so when u call EndInvoke, your thread is not blocked. Instead, you decided your thread should wait the new thread to finish (example: the new thread generates some data your thread needs, i mean data dependency, clearly the best one to have between threads if some). So "you should never kick somebody in the jellies" turns to be "you quite often kick somebody in the jellies". The callback method is quite useful when the calling thread is a user interface thread. But if not, maybe you don´t want to use the callback. And, as far as i know if you use IsComplete only to wait it says "true" inside a while, and then call EndInvoke, I don´t see any difference. Not saying that you are lazy, but that your example obscure the point, and contributes to mistaken conclusions. But of course, you don´t have to agree with me.

Actually, I am lazy, and you are right -- the point of APM is to do multiple things at the same time. So waiting for your async op to finish is pointless.

And, in the end, if you spinlock on IsCompleted until it returns true and then call EndInvoke it produces the same result (blocking your thread's progression) as just calling EndInvoke. But even though they produce the same result they are two different ways to get to that result. For instance, if you wish to show a progress bar (or some other animation) to the user while waiting for an async operation to finish, you could do the following:

As you can see, we are accomplishing something as we spinlock on the IsCompleted property. You cannot do this by calling EndInvoke, since it blocks the calling thread. That's why you can consider them to be two different types of rendezvous techniques. Hope that is more clear than the article. Good programming!

...In the third example: I believe there should be a delegate - right now there is only the name of a function.

True, but it works. There are some times that you can pass a method group as a delegate, and some times you cannot. I haven't figured out the rules behind it; I suppose the C# compiler knows what you're trying to do with some specifc types of delegates (events work the same exact way -- someObject.Event += SomeMethod;). This is one of those cases.

I'm using Microsoft Visual Studio 2003 (7.1.x) and to me it never works if I do not wrap a method into a delegate. I've tried your code, got the expected compile error, corrected and posted my version in a reply to one of the messages above.