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)

No comments: