Polling or Waiting for Completion
In the programming model just shown, when the client calls EndInvoke(), the client is blocked until the asynchronous method returns. This may be fine if the client has a finite amount of work to do while the call is in progress, and if, once that work is done, the client cannot continue its execution without the returned value or the output parameters of the method, or even just the knowledge that the method call has completed. However, what if the client only wants to check if the method execution is completed? What if the client wants to wait for completion for a fixed timeout, do some additional finite processing, and then wait again? .NET supports these alternative programming models to calling EndInvoke().
The IAsyncResult interface object returned from BeginInvoke() has the AsyncWaitHandle property, of type WaitHandle. WaitHandle is actually a .NET wrapper around a native Windows waitable event handle. WaitHandle has a few overloaded wait methods. For example, the WaitOne() method only returns once the handle is signaled. Listing 4 demonstrates using WaitOne().
|The same object code handles both the synchronous and the asynchronous invocation cases|
Logically, Listing 4
is identical to Listing 1,
which only called EndInvoke()
. If the method is still executing, then WaitOne()
will block. If by the time WaitOne()
is called, the method execution is complete, then WaitOne()
will not block, and the client proceeds to call EndInvoke()
for the returned value. The important difference between Listing 4
and Listing 1
is that the call to EndInvoke()
in Listing 4
is guaranteed not to block its caller.
demonstrates a more practical way to use WaitOne()
, by specifying a timeout condition (10 milliseconds in this example). When you specify a timeout, WaitOne()
will return when the method execution completes or when the timeout has elapsed, whichever of these two conditions occurs first.
uses another handy property of IAsyncResult called IsCompleted
, which lets you find out the status of the call without waiting or blocking. You can even use IsCompleted
in a strict polling mode:
while(asyncResult.IsCompleted == false)
/*Do some work */
This has all the adverse effects of polling (consuming CPU power for nothing), so I suggest you avoid using IsCompleted
this way. The AsyncWaitHandle
property really shows its usefulness when you use it to manage multiple concurrent asynchronous methods in progress. You can use the WaitAll()
static method of the WaitHandle class to wait for completion of multiple asynchronous methods, as shown in Listing 6
To use WaitAll()
you need to construct an array of handles. Note that you still need to call EndInvoke()
to access returned values. Instead of waiting for all the methods to return, you can choose to wait for any of them to return using the WaitAny()
static method of the WaitHandle class:
Similar to WaitOne()
, both WaitAll()
have a few overloaded versions, which let you specify a timeout to wait instead of waiting indefinitely.
Using the Completion Callback Method
Instead of blocking, waiting, or polling for the asynchronous method completion, .NET also offers another programming model altogether: callbacks
. The idea is simple: the client provides .NET with a method, and requests that .NET call that method back when the asynchronous method completes. The client can provide a callback instance method or static method, and have the same callback method handle completion of multiple asynchronous methods. The only requirement is that the callback method has the following signature:
void <Name>(IAsyncResult asyncResult);
The convention for a callback method name is to prefix it with On
, for example, OnAsyncCallBack()
, and so on. Here is how the callback mechanism works: As explained previously, .NET uses a thread from the thread pool to execute the method dispatched via BeginInvoke()
. When the asynchronous method execution is completed, instead of quietly returning to the pool, the worker thread calls the callback method.
To use a callback method, the client needs to provide BeginInvoke()
with a delegate that targets the callback method. That delegate is provided as the next to last parameter to BeginInvoke()
, and is always of type AsyncCallback. AsyncCallback is a .NET-provided delegate from the System namespace, defined as:
public delegate void AsyncCallback(
demonstrates asynchronous call management using a completion callback method.
Unlike the previous programming models, when you use a completion callback method, there is no need to save the IAsyncResult object returned from BeginInvoke()
. Why? Because when .NET calls the callback method, .NET provides the IAsyncResult object as a parameter. Note in Listing 7
the use of a downcast of the IAsyncResult parameter to an AsyncResult class to get the original delegate used to dispatch the call. You need that delegate to call EndInvoke()
. Because .NET provides a unique IAsyncResult object per asynchronous method, you can channel multiple asynchronous method completions to the same callback method, even using the same AsyncCallback delegate:
Calculator calc = new Calculator();
callback = new AsyncCallback(OnMethodCompletion);
oppDel1 = new BinaryOperation(calc.Add);
oppDel2 = new BinaryOperation(calc.Add);
Callback completion methods are by far the preferred model in any event-driven application. An event-driven
application has methods that trigger events (or dispatch requests, post and process messages) and methods that handle these requests and fire their own events as a result. Writing an application as event driven makes it a lot easier to manage multiple threads, events, and messages, and allow for scalability, responsiveness, and performance. .NET asynchronous calls management using callback completion methods fits into such an architecture like a hand in a glove. The other options (waiting, blocking, polling) are available for completeness sake, for applications that are very strict, predictable, and deterministic in their execution flow. I recommend that you use completion callback methods whenever possible.
Passing State Information
I ignored the last parameter to BeginInvoke()
, object asyncState
, up until now. It is provided as a generic container for whatever you need to use it for. .NET calls this container a state object
. The party handling the method completion can access the container objects via the object AsyncState
property of IAsyncResult. Although you can certainly use a state object with any of the .NET asynchronous models (blocking, waiting, polling), it is most useful in conjunction with completion methods. The reason is simple: in all the other programming models, it is up to you to manage the IAsyncResult object, and managing an additional container is not that much of an added liability. When you are using a completion callback, the container object is the only way to pass in additional parameters to the callback method, because its signature is pre-determined by .NET. Listing 8
demonstrates using a state object to pass an integer value as an additional parameter to the completion callback method. Note that the callback method must downcast the AsyncState
property to the actual type it expects.