Explaining Delegates in C# - Part 4 (Asynchronous Callback - Way 1)

So far, I have discussed about Callback, Multicast delegatesEvents using delegates, and yet another example of events using delegates. In this post, I will take this topic one step forward and show you an example of Asynchronous callbacks using delegates.

Let's say a client calls a method that takes 40 minutes to complete, how do we communicate with the client?

Option 1> Keep showing that busy Cursor for 40 minutes!!!

Option 2> Keep updating the client with appropriate messages, like... "oh yes... we might take another light year to complete, please wait... oh common..... show some patience... do yoga... etc...  etc...."

Option 3> Tell the client that "okay Sir... you are done with your part... go away take a vacation. Whenever I'm done I will let you know"

As you may agree... Option 3 is a more effective way. Not because it gives you enough time to complete your job , but because the client is not just waiting on you. He is gone after giving you the job and may be doing something else in life. HE is FREE... and FREEDOM is good 

Before we proceed, let's see what happened to the delegate from my previous post (in ILDASM)...

Take a look in the figure above and notice that just creating the delegate in your code, actually created a class behind the scenes with a few methods called Invoke, BeginInvoke, and EndInvoke!!!!! Pretty smart, right?

<Snippet from MSDN>

Delegates enable you to call a synchronous method in an asynchronous manner. When you call a delegate synchronously, the Invoke method calls the target method directly on the current thread. If the BeginInvoke method is called, the common language runtime (CLR) queues the request and returns immediately to the caller. The target method is called asynchronously on a thread from the thread pool. The original thread, which submitted the request, is free to continue executing in parallel with the target method. If a callback method has been specified in the call to the BeginInvoke method, the callback method is called when the target method ends. In the callback method, the EndInvoke method obtains the return value and any input/output or output-only parameters. If no callback method is specified when calling BeginInvoke, EndInvoke can be called from the thread that called BeginInvoke.

</Snippet from MSDN>

Asynchronous calls has two important parts... BeginInvoke and EndInvoke. Once BeginInvoke is called, EndInvoke can be called anytime. The catch is that EndInvoke is a blocking call. Thus, it would block the calling thread until it is complete. There are several ways in which you could work with BeginInvoke and EndInvoke at tandem. In this post we will take a look at the first way!!

The following code is almost like a husband telling his wife (whom he is dropping in a mall for some shopping!!)...

You know honey, I have a lot of work to do. Why don't you help me up by doing something that you can do pretty well . In the meantime, I will take care of some other stuff. As soon as I am done, I promise I will pick you up.

The good thing with this approach is that the wife (in our case, a new thread) is doing something, while the main thread can continue to do something else. The catch is that, the husband (main thread) must be aware that once its job is done, it will have to wait (blocking call) for his wife (the other thread)!! Just like I do, well... almost always 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

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... {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. Call the delegate's EndInvoke
    public class TortoiseConsumer
    {
        public 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(30, out threadId, null, null);
            Console.WriteLine("After making the Async call... Thread ID = {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Perform more work as the other thread works...");
            for (int i = 30; i > 1; i--)
            {
                Thread.Sleep(1000);
                Console.WriteLine("{0}...", i);                                
            }
            Console.WriteLine("Waiting for the async call to return now...");
            //Notice that this call will be a blocking call
            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.

转:http://www.dotnetscraps.com/dotnetscraps/post/Explaining-Delegates-in-C-Part-4-(Asynchronous-Callback-Way-1).aspx

posted @ 2013-09-13 14:47  shuaixf  阅读(330)  评论(0编辑  收藏  举报