[ Team LiB ] |
Recipe 15.6 Being Notified of the Completionof an Asynchronous DelegateProblemYou need a way of receiving notification from an asynchronously invoked delegate that it has finished. However, it must be more flexible than the notification schemes in the previous two recipes (Recipe 15.4 and Recipe 15.5). This scheme must allow your code to continue processing without having to constantly call IsCompleted in a loop or to rely on the WaitOne method. Since the asynchronous delegate will return a value, you must be able to pass this return value back to the invoking thread. SolutionUse the BeginInvoke method to start the asynchronous delegate, but use the first parameter to pass a callback delegate to the asynchronous delegate: using System; using System.Threading; public class AsyncAction { public void CallbackAsyncDelegate( ) { AsyncCallback callBack = new AsyncCallback(DelegateCallback); AsyncInvoke method1 = new AsyncInvoke(TestAsyncInvoke.Method1); IAsyncResult asyncResult = method1.BeginInvoke(callBack, method1); // No need to poll or use the WaitOne method here, so return to the calling // method. return; } private static void DelegateCallback(IAsyncResult iresult) { AsyncResult asyncResult = (AsyncResult)iresult; AsyncInvoke method1 = (AsyncInvoke)asyncResult.AsyncDelegate; int retVal = method1.EndInvoke(asyncResult); Console.WriteLine("retVal (Callback): " + retVal); } } This callback delegate will call the DelegateCallback method on the thread the method was ultimately invoked on when the asynchronous delegate is finished processing. The following code defines the AsyncInvoke delegate and the asynchronously invoked static method TestAsyncInvoke.Method1: public delegate int AsyncInvoke( ); public class TestAsyncInvoke { public static int Method1( ) { Console.WriteLine("Invoked Method1"); return (1); } } DiscussionThe asynchronous delegates in this recipe are created and invoked in the same fashion as the asynchronous delegate in Recipe 15.4. Instead of using the IsCompleted property to determine when the asynchronous delegate is finished processing (or the WaitOne method to block for a specified time while the asynchronous delegate continues processing), this recipe uses a callback to indicate to the calling thread that the asynchronous delegate has finished processing and that its return value, ref parameter values, and out parameter values are available. Invoking a delegate in this manner is much more flexible and efficient than simply polling the IsCompleted property to determine when a delegate finishes processing. When polling this property in a loop, the polling method cannot return and allow the application to continue processing. A callback is also better than using a WaitOne method, since the WaitOne method will block the calling thread and allow no processing to occur. You can break up the WaitOne method into a limited number of wait cycles as in Recipe 15.5, but this is simply a merging of the polling technique with the WaitOne operation. The CallbackAsyncDelegate method in this recipe makes use of the first parameter to the BeginInvoke method of the asynchronous delegate to pass in another delegate that contains a callback method to be called when the asynchronous delegate finishes processing. After calling BeginInvoke, this method can now return and the application can continue processing; it does not have to wait in a polling loop or be blocked while the asynchronous delegate is running. The AsyncInvoke delegate that is passed into the first parameter of the BeginInvoke method is defined as follows: public delegate void AsyncCallback(IAsyncResult ar) When this delegate is created, as shown here, the callback method passed in, DelegateCallback, will be called as soon as the asynchronous delegate completes: AsyncCallback callBack = new AsyncCallback(DelegateCallback); DelegateCallback will not run on the same thread BeginInvoke ran on. This callback method accepts a parameter of type IAsyncResult. You can cast this parameter to an AsyncResult object within the delegate and use it to obtain information about the completed asynchronous delegate, such as its return value, any ref parameter values, and any out parameter values. If the delegate instance that was used to call BeginInvoke is still in scope, you can just pass the IAsyncResult to the EndInvoke method. In addition, this object can obtain any state information passed into the second parameter of the BeginInvoke method. This state information can be any object type. The DelegateCallback method casts the IAsyncResult parameter to an AsyncResult object and obtains the asynchronous delegate that was originally called. The EndInvoke method of this asynchronous delegate is called to process any return value, ref parameters, or out parameters. If any state object was passed in to the BeginInvoke method's second parameter, it can be obtained here through the following line of code: object state = asyncResult.AsyncState; See AlsoSee the "AsyncCallback Delegate" topic in the MSDN documentation. |
[ Team LiB ] |