Thursday 5 May 2011

Dot.Net: Asynch Method Call Sample

Question:
Do you have an example of a standard asynchronous method call with callback in CSharp ?
Any event driven asynchronous sample in dot.net ??


Answer:
There are some simple ways to do it, called "fire and forget" (you don't wait for the completion of your method and you get no notification)
And then there is a more complex way to call methods asynchronously where you pass your asynchronous method call a callback function that is called on completion.

A typical usage sample
The idea here is, that you have a long running task and want to set it on a different thread. Once the task is completed you want to get a call that allows you to proceed with the results.

internal void Run()
{
    Console.WriteLine("Start Run");
    Calculator calc = new Calculator();
    calc.FindPrimeCompleted +=new EventHandler<FindPrimeCompletedEventArgs>(calc_FindPrimeCompleted);
    calc.FindPrimeAsync(4);
    Console.WriteLine("Finished Run");

    Console.ReadLine();
}

void calc_FindPrimeCompleted(object sender, FindPrimeCompletedEventArgs e)
{
    int result = e.Result;
    Console.WriteLine("The result is : " + result);
    if (e.Error != null)
    {
        Console.WriteLine("Error : " + e.Error.Message);
    }
}


// here is the arguments definition
public class FindPrimeCompletedEventArgs : AsyncCompletedEventArgs
{
    public int Result { get; private set; }

    public FindPrimeCompletedEventArgs(Exception err, bool cancelled, object userState, int res)
        : base(err, cancelled, userState)
    {
        this.Result = res;
    }
}


To make this work you have to create your worker class, add an event and a asynchronous method.

Follow these steps:
  1. 1) Create a simple method
  2. 2) Create an asynchronous method
  3. 3) Create a delegate instance for your asynch method
  4. 4) Create a callback instance with your method callback
  5. 4a) Create a callback function
  6. 5) Invoke delegate asynchronously
  7. 6a) Define event
  8. 6b) Implement event method

Here is an example
public class Calculator
{
    private readonly object _sync = new object();
    private bool _busy;
    // 1) simple method
    private int FindPrime(int x)
    {
        // fake prime search
        Thread.Sleep(3000);
        //throw new ApplicationException("Very BAD error");
        return x * 11 -1;
    }
    // 2) asynchronous method
    public void FindPrimeAsync(int x)
    {
        // 3) create delegate
        Func<int, int> worker = new Func<int, int>(FindPrime);
        // 4) create a callback instance with your method callback
        AsyncCallback completedCallback = new AsyncCallback(FindPrimeCompletedCallback);
        // AsyncOperation is responsable to marshal the calls to the appropriate thread or context.
        // you can pass a UserSuppliedState object to uniquely identify your asynchronous operation
        AsyncOperation async = AsyncOperationManager.CreateOperation(null);
        // 5) invoke delegate asynchronously, passing the orignial arguemnt(s) x, and then the completedCallback and AsyncOperation manager
        worker.BeginInvoke(x, completedCallback, async);

    }
    // 4a Create a callback function
    private void FindPrimeCompletedCallback(IAsyncResult ar)
    {
        Func<int, int> worker = (Func<int, int>)((AsyncResult)ar).AsyncDelegate;
        AsyncOperation async = (AsyncOperation)ar.AsyncState;
        Exception err = null;
        bool cancelled = false;
        object userState = null;
        int res = 0;

        // finish the asynchronous operation and get result
        try
        {
            res = worker.EndInvoke(ar);
        }
        catch (Exception ex)
        {
            err = ex;
        }

        // raise the completed event
        FindPrimeCompletedEventArgs completedArgs = new FindPrimeCompletedEventArgs(err, cancelled, userState, res);
        async.PostOperationCompleted( e => OnFindPrimeCompleted((FindPrimeCompletedEventArgs)e), completedArgs);

    }
    // 6b) Implement event method
    protected virtual void OnFindPrimeCompleted(FindPrimeCompletedEventArgs e)
    {
        if (FindPrimeCompleted != null)
            FindPrimeCompleted(this, e);
    }
    // 6a) Define event
    public event EventHandler<FindPrimeCompletedEventArgs> FindPrimeCompleted;

}


No comments: