7/8/12

Asynchronous Programming in .NET - Part 2

As I mentioned in my previous article Asynchronous Programming in .NET - Part 1, delegates can be used to facilitate asynchronous calls. The Client creates an instance of the delegate class, specifies the method that is being delegated, and then invokes the delegate which handles the execution of the specified method on a separate thread pool thread.

The drawback to this is that the calling code needs to do all of the following:
  • Be aware of the Delegate and its signature
  • Create a method with the same signature as the delegate
  • Create an instance of the delegate and assign the method above to it
  • Call BeginInvoke on the delegate to begin the task
  • Call EndInvoke on the delegate to capture the results

In the case of long running tasks like Web Service calls, Web Requests and network access calls, it is obvious that the Client would prefer to invoke the methods asynchronously. In such cases it makes sense to expose asynchronous methods to the Client. The Client can then call these asynchronous methods instead of having to do all the work above involving delegate calls.

Following is an example of such a class.


public class SportsDelegate
    {
        
        private delegate void TeamOwnerDelegate(string team,out string owner);
        
        //the method that is called by the Client to invoke the delegate
        public IAsyncResult FindOwnerName(string team,out string owner,AsyncCallback callback,object state)
        {
            //specify the method that needs to be delated
            TeamOwnerDelegate objdel = TeamOwnerBegin; 
            return objdel.BeginInvoke(team, out owner, callback, null);
        }       

        //the method that is delegated to the thread pool thread
        private void TeamOwnerBegin(string team, out string owner)
        {
            //this method sets the value for the ouput variable "owner"
            //we are keeping it simple - this could have been a call to 
            //the DB or some external datasource if necessary
            switch (team.ToUpper())
            {
                case "DALLASCOWBOYS":
                owner = "Jerry Jones";
                break;
                case "DALLASMAVERICKS":
                owner = "Mark Cuban";
                break;
                default:
                owner = "Nolan Ryan"; //texas rangers owner is the default
                break;
            }
            
        }

        //this is the method called by the Client to get the results
        public string GetOwnerName(IAsyncResult result)
        {
            AsyncResult objreturn = (AsyncResult)result;
            TeamOwnerDelegate objdel = (TeamOwnerDelegate)objreturn.AsyncDelegate;
            string owner = null;
            
            try
            {
                objdel.EndInvoke(out owner, result);
                return owner;
            }
            catch (Exception ex)
            {
                return null;
            }
        }       
    }

The class above exposes two public asynchronous methods to the client, one called "FindOwnerName" and the other is called "GetOwnerName". All the client needs to do is call these public methods as needed. The Client does not need to be aware of the delegate and does not have to do any extra work to make asynchronous calls. The SportsDelegate class handles all the work related to creating/instantiating the delegate and calling the BeginInvoke and EndInvoke methods as needed.

Following is how the client code looks:

public class HomeController : Controller
    {
        
        public ActionResult Index()
        {           
            string owner;
            SportsDelegate obj = new SportsDelegate();
            obj.FindOwnerName("dallascowboys", out owner, MyCallBack, null);       
            return View();
        }

         private void MyCallBack(IAsyncResult result)
        {
            //this needs to be thread safe since this will be 
            //called by the thread pool thread
            SportsDelegate obj = new SportsDelegate();
            string owner = obj.GetOwnerName(result);
            ViewBag.Message = string.Format("My owner name is is {0}", owner);
        }
}

The code above calls the FindOwnerName method to start processing. Once that asynchronous method completes, it invokes the Callback method specified by the Client. In the callback method, the Client makes a call to the GetOwnerName method to get the results.

NOTE:
A certain amount of thread synchronization will be necessary to handle the interaction between the Client and the SportsDelegate class


Examples of .NET framework classes that provide asynchronous methods are Stream and Web Service Proxy classes.(reference: programming .NET components by Juval Lowy)

Asynchronous programming in .NET - Part 1

Asynchronous programming in .NET is facilitated via Delegates. A delegate is a mechanism that is used to delegate a method call as shown in the code below.



//my custom class in the class library where i have defined the delegate
public class MyCustomDelegates
{
 public delegate string MyFootballDelegate(string team);
}

public class Client
{
 public void ProcessDelegates()
 {
   MyFootballDelegate objDelegate = Dallas;
   objDelegate("cowboys");
 }
 
 private string Dallas(string team)
 {
  //do something here
  return string.format("{0} - {1}","Dallas", team);
 }
}


In the code above, the Client creates an instance of the Delegate called MyFootballDelegate and assigns a method Dallas to it. The Client then invokes the delegate passing in an input value called "cowboys".So what the client is doing is delegating the act of calling that method Dallas to an instance of the MyFootballDelegate class(the compiler internally converts a delegate declaration into a class).

BeginInvoke() and EndInvoke()

In the code above the Client blocks until the method Dallas completes, so this use of delegates is not really asynchronous. To make the call asynchronous the Client needs to invoke BeginInvoke and EndInvoke methods on the delegate as follows:


public class Client
{
 public void ProcessDelegates()
 {
   MyFootballDelegate objDelegate = Dallas;
   iAsyncResult objAsyncresult = objDelegate.BeginInvoke("cowboys",null,null);
   string result = objDelegate.EndInvoke(objAsyncresult);
 }
 
 private string Dallas(string team)
 {
    //do something here
           return string.format("{0} - {1}","Dallas", team);
 }
}


The call to BeginInvoke uses a thread from the .NET Thread pool and queues up the delegate call. So it blocks the calling code for only a brief moment.The Client then calls the EndInvoke to get the result from the method call.

Completion Callback Methods

The Client, instead of calling EndInvoke explicitly, can pass a callback method to the BeginInvoke method that is invoked as soon as the method Dallas ends. Code is as follows:

public class Client
{
       public void ProcessDelegates()
       {
   MyFootballDelegate del= Dallas;
          iAsyncResult obj = del.BeginInvoke("cowboys",DallasCallback,null);   
 }
 
 private string Dallas(string team)
 {
          //do something here
          return string.format("{0} - {1}","Dallas", team);
        }
 
 //the callback method must have the following signature
 private void DallasCallback(IAsyncResult result)
 {
  //call to EndInvoke happens here and the result is obtained here
 }
}


NOTE:
In the code above, the Client specifies the method DallasCallback as the callback in the BeginInvoke call.So the thread from the thread pool that executes the Method Dallas,will also call the callback method as soon as the Dallas method completes. So the code in the DallasCallback method needs to be thread-safe.Besides, a certain amount of thread synchronization will be necessary to handle the interaction between the calling code and the delegate call/callback methods related code

The code in the callback method needs to call EndInvoke on the delegate as follows:


private void DallasCallback(IAsyncResult result)
{
 //call to EndInvoke happens here
 AsyncResult objreturn = (AsyncResult)result;
 MyFootballDelegate objDelegate = (MyFootballDelegate)objreturn.AsyncDelegate;

        //the value returned by method Dallas
 string result = objDelegate.EndInvoke(result); 
}