Sunday, June 3, 2012

Asynchronous Programming and Callback Methods with Delegates in C#

My previous post was about Delegates in C# and today I thought of writing about one nice technique which is available with Delegates, and that is "Asynchronous Programming". In one of my previous posts, I wrote about Asynchronous Programming with C# 5.0 and that is with "async" modifier and "await" operator. 

Using Delegates we can call a synchronous method in an asynchronous manner and Callback methods are used to notify the caller, when an asynchronous work is completed. First let's get an idea on how these synchronous and asynchronous methods works.

When we call a synchronous method it will get executed on the same thread as the caller. But what happens here is, caller thread gets blocked while the called method is active. When we call a method in an asynchronous way, the .NET framework obtains a thread from the thread pool for the method invocation and delivers the in parameters passed by the calling code. The asynchronous thread can then run the method in parallel to the calling thread. Here what happens is caller thread is not blocked. The call returns immediately to the caller. If the asynchronous method returns some value, the calling thread must be able to handle it. The .NET asynchronous feature supports two mechanisms: that calling thread can either ask for the results, or the asynchronous method can deliver the results to the calling thread when the results are ready.

Delegate's do this asynchronous call invocation using delegate's BeginInvoke method.
First we have to define a delegate with the same signature as the method we want to call. The common language runtime automatically defines BeginInvoke and EndInvoke methods for this delegate, with the appropriate signatures. 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.

I think it's better if we go by examples.

Let's take the following method under the class i have created named "MyClass".
public class MyClass
{
    public string MyMethod(string s)
    {
        Thread.Sleep(5000);
        return "Hello" + s;
    }
}
I have a public method, which will return a string and accepts a string. Then I am creating a delegate for this method.
public delegate string MyDelegate(string s);
Then I am creating my delegate object.
MyClass oMyClass = new MyClass();
MyDelegate oMyDelegate = new MyDelegate(oMyClass.MyMethod);
Now I am all done for calling the method asynchronously. As I have told you, the CLR automatically defines a BeginInvoke method for this delegate object which would have a signature like this.
IAsyncResult oMyDelegate.BeginInvoke(string s, AsyncCallback callback, Object @object);
In here, the first parameter is the methods parameter. The second parameter is an AsyncCallback delegate that references a method to be called when the asynchronous call completes. The third parameter is a user-defined object that passes information into the callback method.

Let's take a complete example.
using System;
using System.Threading;

namespace MyDelegateExample
{
    public delegate string MyDelegate(string s);

    class Program
    {
        static void Main(string[] args)
        {
            MyClass oMyClass = new MyClass();
            MyDelegate oMyDelegate = new MyDelegate(oMyClass.MyMethod);
            Console.WriteLine("Going to call the method Asynchronously.");
            oMyDelegate.BeginInvoke("Jaliya", new AsyncCallback(oMyClass.MyCallbackMethod), oMyDelegate);
            Console.WriteLine("Back on Main.");
            Thread.Sleep(15000);
        }
    }

    public class MyClass
    {
        public string MyMethod(string s)
        {
            Thread.Sleep(5000);
            return "Hello " + s;
        }

        // call back method to capture results
        public void MyCallbackMethod(IAsyncResult iar)
        {
            // cast the state object back to the delegate type
            MyDelegate del = (MyDelegate)iar.AsyncState;

            // call EndInvoke on the delegate to get the results
            string result = del.EndInvoke(iar);

            // display the results
            Console.WriteLine("Delegate returned result: {0}", result);
            Console.WriteLine("Asynchronous Call Completed.");
        }
    }
}

Output :

Output
In here what happens is in my BeginInvoke call, I have mentioned my Callback method. My main program will continue running in parallel and when the asynchronous call is completed, it will execute the "MyCallbackMethod". In that method, I have called the EndInvoke method to retrieve the results of the asynchronous call. I have put a thread sleep in my Main to keep the program running. So I can see the output from my asynchronous call.

Let's take this example.
using System;
using System.Threading;

namespace MyDelegateExample
{
    public delegate string MyDelegate(string s);

    class Program
    {
        static void Main(string[] args)
        {
            MyClass oMyClass = new MyClass();
            MyDelegate oMyDelegate = new MyDelegate(oMyClass.MyMethod);
            Console.WriteLine("Going to call the method Asynchronously.");
            IAsyncResult IAsyncResult = oMyDelegate.BeginInvoke("Jaliya", null, null);
            Console.WriteLine("Back on Main.");
            IAsyncResult.AsyncWaitHandle.WaitOne();
            string result = oMyDelegate.EndInvoke(IAsyncResult);
            Console.WriteLine(result);
            Console.ReadLine();        
        }
    }

    public class MyClass
    {
        public string MyMethod(string s)
        {
            Thread.Sleep(5000);
            return "Hello " + s;
        }
    }
}

Output :

Output

The difference here is there is no callback method.
AsyncResult.AsyncWaitHandle.WaitOne();
will hold the Main from executing further until asynchronous call completes. After that completes only it will move executing forward.

I hope you all got a good understanding in how Asynchronous Programming and Callback Methods works with Delegates.

Appreciate your feedback.

Happy Coding.

Regards,
Jaliya