Asynchronous Delegates


A simple way to create a thread is by defining a delegate and invoking the delegate asynchronously. In Chapter 7, “Delegates and Events,” you’ve seen delegates as type-safe references to methods. The Delegate class also supports invoking the methods asynchronously. Behind the scenes, the Delegate class creates a thread that fulfills the task.

Tip 

The delegate uses a thread pool for asynchronous tasks. Thread pools are discussed later.

To demonstrate the asynchronous features of delegates, start with a method that takes a while to complete. The method TakesAWhile needs at least the number of milliseconds passed with the second argument to finish because of the Thread.Sleep() method.

  static int TakesAWhile(int data, int ms) {    Console.WriteLine("TakesAWhile started");    Thread.Sleep(ms);    Console.WriteLine("TakesAWhile completed");    return ++data; } 

To invoke this method from a delegate, a delegate with the same parameter and return types must be defined, as shown by the delegate TakesAWhileDelegate:

  public delegate int TakesAWhileDelegate(int data, int ms); 

Now you can use different techniques, invoking the delegate asynchronously and having the result returned.

Polling

One technique is to poll and check if the delegate has already finished its work. The created delegate class provides the method BeginInvoke(), where you can pass the input parameters defined with the delegate type. BeginInvoke() always has two additional parameters of type AsyncCallback and object, which are discussed later. What’s important now is the return type of BeginInvoke(): IAsyncResult. With IAsyncResult, you can get information about the delegate, and also verify if the delegate already finished its work, as is done with the IsCompleted property. The main thread of the program continues the while loop as long as the delegate hasn’t completed its work.

  static void Main() {    // synchronous    // TakesAWhile(1, 3000);    // asynchronous    TakesAWhileDelegate d1 = TakesAWhile;    IAsyncResult ar = d1.BeginInvoke(1, 3000, null, null);    while (!ar.IsCompleted)    {       // doing something else in the main thread       Console.Write(".");       Thread.Sleep(50);    }    int result = d1.EndInvoke(ar);    Console.WriteLine("result: {0}", result); } 

Running the application, you can see the main thread and the thread of the delegate running concurrently, and the main thread stops looping after the delegate thread completes:

 .TakesAWhile started ...........................................................TakesAWhile completed result: 2

Instead of examining if the delegate is completed, you can also just invoke the EndInvoke() method of the delegate type after you’re finished with the work that can be done by the main thread. EndInvoke() itself waits until the delegate has completed its work.

Important 

If you don’t wait for the delegate to complete its work and end the main thread before the delegate is finished, the thread of the delegate will be stopped.

Wait Handle

Another way to wait for the result from the asynchronous delegate is by using the wait handle that is associated with IAsyncResult. You can access the wait handle with the AsyncWaitHandle property. This property returns an object of type WaitHandle, where you can wait for the delegate thread to finish its work. The method WaitOne() accepts a timeout with the optional first parameter, where you can define the maximum time you want to wait; here it is set to 50 milliseconds. If a timeout occurs, WaitOne() returns with a false and the while loop continues. If the wait is successful, the while loop is exited with a break, and the result is received with the delegate EndInvoke() method.

 static void Main() {    TakesAWhileDelegate d1 = TakesAWhile;    IAsyncResult ar = d1.BeginInvoke(1, 3000, null, null);    while (true)    {       Console.Write(".");       if (ar.AsyncWaitHandle.WaitOne(50, false))       {          Console.WriteLine("Can get the result now");          break;       }    }    int result = d1.EndInvoke(ar);    Console.WriteLine("result: {0}", result); }

Tip 

You can read more information about wait handles later in the synchronization section of this chapter.

Asynchronous Callback

The third version of waiting for the result from the delegate uses an asynchronous callback. With the third parameter of BeginInvoke, you can pass a method that fulfills the requirements of the AsyncCallback delegate. The AsyncCallback delegate defines a parameter of IAsnycResult and a void return type. Here, the address of the method TakesAWhileCompleted is assigned to the third parameter that fulfills the requirements of the AsyncCallback delegate. With the last parameter, you can pass any object for accessing it from the callback method. It’s useful to pass the delegate instance itself, so the callback method can use it to get the result of the asynchronous method.

Now the method TakesAWhileCompleted() is invoked as soon as the delegate TakesAWhileDelegate has completed its work. There’s no need to wait for a result inside the main thread. However, you may not end the main thread before the work of the delegate threads is finished unless you don’t have a problem with that the delegate threads are just stopped.

 static void Main() {    TakesAWhileDelegate d1 = TakesAWhile;    d1.BeginInvoke(1, 3000, TakesAWhileCompleted, d1);    for (int i = 0; i < 100; i++)    {       Console.Write(".");       Thread.Sleep(50);    } }

The method TakesAWhileCompleted() is defined with the parameter and return type specified by the AsyncCallback delegate. The last parameter passed with the BeginInvoke() method can be read here using ar.AsyncState. With the TakeAWhileDelegate you can invoke the EndInvoke method to get the result.

  static void TakesAWhileCompleted(IAsyncResult ar) {    if (ar == null) throw new ArgumentNullException("ar");    TakesAWhileDelegate d1 = ar.AsyncState as TakesAWhileDelegate;    Trace.Assert(d1 != null, "Invalid object type");    int result = d1.EndInvoke(ar);    Console.WriteLine("result: {0}", result); } 

Important 

With a callback method you have to pay attention that this method is invoked from the thread of the delegate and not the main thread.

Instead of defining a separate method and pass it to the BeginInvoke() method, anonymous methods are very handy in this place. With the delegate keyword you can define an anonymous method with the parameter of type IAsyncResult. Now there’s no need to assign a value to the last parameter of the BeginInvoke() method because the anonymous method can directly access variable d1 that is outside this method. However, the anonymous method is still invoked from the thread of the delegate, which is not that clear when defining the method this way.

 static void Main() {    TakesAWhileDelegate d1 = TakesAWhile;    d1.BeginInvoke(1, 3000,       delegate(IAsyncResult ar)       {          int result = d1.EndInvoke(ar);          Console.WriteLine("result: {0}", result);       },       null);    for (int i = 0; i < 100; i++)    {       Console.Write(".");       Thread.Sleep(50);    } }

Tip 

You should use anonymous methods only if the code within is not too big, and the implementation is not required on different places. In such cases, defining a separate method is preferred. Anonymous methods are explained in Chapter 7, “Delegates and Events.”

The programming model and all these options with asynchronous delegates - polling, wait handles, and asynchronous callbacks are not only available with delegates. The same programming model can be found in various places in the .NET Framework. For example, you can send an HTTP Web request asynchronous with the BeginGetResponse() method of the HttpWebRequest class. You can send an asynchronous request to the database with the BeginExecuteReader() of the SqlCommand class. The parameters are similar to those of the BeginInvoke() class of the delegate, and you can use the same mechanisms to get the result.

Tip 

HttpWebRequest is covered in Chapter 35, “Accessing the Internet,” and SqlCommand is discussed in Chapter 25, “Data Access with .NET.”




Professional C# 2005 with .NET 3.0
Professional C# 2005 with .NET 3.0
ISBN: 470124725
EAN: N/A
Year: 2007
Pages: 427

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net