Sunday 23 June 2013

C# - Asynchronous programming using async and await

The asynchronous programming has moved on from the days when we need to use BeginInvoke/EndInvoke and a callback method to call a method asynchronously and get the result once the called method had

finished its execution. Since C# 4.0, there is a much simpler mechanism available to make the asynchronous calls. This is achieved by using async modifier and await keyword.

I tried my hand with asynchronous programming using this feature and have a few observations and would like to see if my understanding is correct. Here is my code

1) The new asynchronous calls rely heavily on tasks. Tasks can be thought of a piece of work which needs to be completed synchronously or asynchronously and might return a value. A task is run

inside a thread. So when we create a new task, effectively what we are doing is creating a new thread (from threadpool) and placing the task inside that new thread. In the code this is achieved

using StartNew method which creates an Action method and starts the task. This is evident with the managed thread id used in the code to show that a new thread is created hosting a new task by StartNew method.

2) The method marked as async are run in the main thread. They are not separate tasks and are the replacements for BeginInvoke and EndInvoke delegate methods. In the following code,

GetAttendeeDetailAsync is an async method executing in the same (main) thread which GetAttendeeDetailTask method asynchronously. The GetAttendeeDetailTask method creates and run a new Task (in a

new thread) and returns a Task<string>.

3) The return type of any async method can be void, Task or Task<T>. Starting from the call to the first async method, which can be called from a method having any return type or an event handler,

the return type of this called method should be void. The reason is that the calling method (ex. event handler) would otherwise need to convert the result returned as Task or Task<T> to appropriate

type. And since the calling method is not marked as async, it cannot use await to type cast the result.

4) In the code the return type of the async method, GetAttendeeDetailTask ,is Task<string>. What it means is that although it returns a string, the compiler wraps it into Task<string> while the

method is still running and the result is still awaited. The method GetAttendeeDetailTask is not an async method and hence should return a Task<string> and not a string type because we are not

using async/await on this method and hence the compiler will not perform the automatic type casts. If you see the value of the returning variable while debugging, it will not contain the actual

return value. What it will have is Status = Running and result = Not Yet Computed. This shows that the method execution is still not completed.

5) Await does the opposite. It casts the result returned as Task<string> to string. It actually unwraps a Task<T> type to the type T and returns the result when the async method execution finishes.

The code is as follows:
------------------------------------------------------------------------------------------------------------

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAndAwait
{

    class ConferenceCall
    {
        public delegate void IntimateAttendeesHandler(bool CallStarted);
        public event IntimateAttendeesHandler CallStartedEventRaisesEvent;  // Declare event in Publisher

        // Raising the event
        public void Run()
        {
            CallStartedEventRaisesEvent(true);
        }
    }

    class Attendee1
    {
        public void SubscribeToEventEvent(ConferenceCall conferenceCall)
        {

            conferenceCall.CallStartedEventRaisesEvent += new ConferenceCall.IntimateAttendeesHandler(SubscribedMethod);
        }

        // Callback Method
        public async void SubscribedMethod(bool CallStarted)
        {
            Console.WriteLine("Attendee1 is online");

            Task<int> Result = GetAttendeeDetailAsync();
            Console.WriteLine("Inside SubscribedMethod waiting.....");
            int length = await Result;
            Console.WriteLine("Main Thread Id:{0}", Thread.CurrentThread.ManagedThreadId.ToString());
            Console.WriteLine(length.ToString());
        }

        // Callback Method

        public async Task<int> GetAttendeeDetailAsync()
        {
            try
            {
                Client objClient = new Client();
                Task<string> GetDetails = objClient.GetAttendeeDetailTask();
                Console.WriteLine("Awaiting in GetAttendeeDetailAsync");
                string retValue = await GetDetails;

                return retValue.Length;
            }
            catch (Exception ex)
            {
                return ex.ToString().Length;
            }
        }

    }

    class Client
    {
        public Task<string> GetAttendeeDetailTask()
        {
            Task<string> retValue = null;
            retValue = Task<string>.Factory.StartNew(() => GetResult());    // Creates and start an asynchronous operation
            return retValue;
        }

        public string GetResult()
        {
            Console.WriteLine("New Thread Id:{0}", Thread.CurrentThread.ManagedThreadId.ToString());
            System.Threading.Thread.Sleep(10000);
            return "Name:CSharp;Age:12";
        }
    }
    class Test
    {

        ConferenceCall conferenceCall = new ConferenceCall();
        Attendee1 attendee1 = new Attendee1();

        public void Run()
        {
            attendee1.SubscribeToEventEvent(conferenceCall);
            conferenceCall.Run();
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main Thread Id:{0}", Thread.CurrentThread.ManagedThreadId.ToString());
            Test objTest = new Test();
            objTest.Run();
            Console.WriteLine("Inside Main_1");
            Console.WriteLine("Inside Main_2");
            Console.ReadLine();

        }
    }

}

The following image shows the value of the returning variable when the method execution is still not finished:


The following is the output of the program. note than the result '18' is returned with a time lag of 10 seconds and is not returned instantaneously because the thread sleeps for 10 seconds.


No comments:

Post a Comment