By now, I have shown the following usages of delegates...
Callback and Multicast delegates
Events
One more Event
Asynchronous Callback - Way 1 - BeginInvoke > EndInvoke
Asynchronous Callback - Way 2 - BeginInvoke > AsyncWaitHandle.WaitOne(x) > EndInvoke > AsynWaitHandle.Close()
In this part, we will take a look at how you could use polling to figure out if the Asynchronous call is complete. So, to carry on with the Husband-Wife analogy from my previous posts, the present scenario would be something like... the husband leaves his wife at the mall for shopping, comes back and is waiting in the parking lot for his wife. He knows that their kid is waiting at their home, so he keeps calling his wife every N minutes, and then calls his kid to say... sorry kiddo, another few moments!!! In the code that follows the person looking at the UI is the kiddo, main thread = husband, and the thread doing asynchronous stuff is wife.
Let's take a look at the code (and read the comments to get a better understanding!).
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Diagnostics; namespace EventAndDelegateDemo { //The delegate must have the same signature as the method. In this case, //we will make it same as TortoiseMethod public delegate string TortoiseCaller(int seconds, out int threadId); public class TortoiseClass { // The method to be executed asynchronously. public string TortoiseMethod(int seconds, out int threadId) { Console.WriteLine("The slow method... executes...on thread {0}", Thread.CurrentThread.ManagedThreadId); for (int i = 0; i < 5; i++) { Thread.Sleep(seconds / 5 * 1000); //Console.WriteLine("The async task is going on thread # {0}", Thread.CurrentThread.ManagedThreadId); } threadId = Thread.CurrentThread.ManagedThreadId; return String.Format("I worked in my sleep for {0} seconds", seconds.ToString()); } } //Now, that we are done with the declaration part, let's proceed to //consume the classes and see it in action //The algorithm would be very simple... // 1. Call delegate's BeginInvoke // 2. Do some work on the main thread // 3. Keep polling using result's IsCompleted property // 4. Call EndInvoke which won't be a blocking call this time! public class TortoiseConsumer { static void Main() { //Instantiate a new TortoiseClass TortoiseClass tc = new TortoiseClass(); //Let's create the delegate now TortoiseCaller caller = new TortoiseCaller(tc.TortoiseMethod); //The asynchronous method puts the thread id here int threadId; //Make the async call. Notice that this thread continues to run after making this call Console.WriteLine("Before making the Async call... Thread ID = {0}", Thread.CurrentThread.ManagedThreadId); IAsyncResult result = caller.BeginInvoke(25, out threadId, null, null); //After calling the method asynchronously, the main thread continues to work... Console.WriteLine("After making the Async call... Thread ID = {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Start the polling... Waiting for the Tortoise method to complete..."); //The IAsynResult interface uses IsCompleted property which you can use to figure out if the call is complete //Notice that this will be a blocking call until the Async call completes. while (result.IsCompleted == false) { Console.Write("."); Thread.Sleep(500); } //Normally, EndInvoke would be a blocking call, but in this case... it won't be. //The reason is that we now know that the Async call is completed! string returnValue = caller.EndInvoke(out threadId, result); Console.WriteLine(" The call got executed on thread {0}", threadId); Console.WriteLine("The value returned was - {0}", returnValue); } } }
I will discuss about more ways of doing asynchronous programming in some of my next posts.