Dot Net

Syrinx .NET Development Blog
Need help on your project? info@syrinx.com, or toll free (888) 579-7469, press 1

News



Need help with your .NET Development project?

Syrinx works with clients throughout New England to architect, design, develop, and deploy .NET Applications. Working on fully outsourced projects, as part of your team, helping to train your team, or rescuing projects in trouble, we are comfortable doing it all. Projects from a couple weeks to several months in duration, reference clients available. Contact us today - info@syrinx.com, or toll free (888) 579-7469 and press 1 to speak to someone now!

How to be in two places at the same time - Asynchronous method calls

 

Overview:

Oftentimes developers are faced with time consuming tasks that appear to make the software being developed run slowly.  The majority of code developed is sequential and synchronous.  That is, step one is processed and when it is completed step two is processed.  There are, however times when a process that is not critical to the current output delays the application (for instance, after retrieving a large dataset from a database for a web application and rendering some portion of it to the page we might want to cache the dataset so as not to have to make another call to the database.)  Situations such as these are perfect opportunities to implement asynchronous logic and have the application be a bit faster.

In the following console application I implemented an artificial call that does nothing but wait a set amount of time before continuing with its execution.  This is intended to mimic the effort required to execute some piece of useful code within our application (like the caching example in the previous paragraph).  The output following the code is very clear as to the action and sequence being taken within the code.

To implement an asynchronous call one delegate is required.  It must have the same signature as the method that you will be calling, in this case the DoSomeUsefulWorkDelegate.  This delegate allows us to execute the method using the BeginInvoke method.  This method starts the function on the ThreadPool where it gets a new thread to perform its work.

If some clean-up or exception handling needs to be performed then another delegate is also required.  This second delegate is a System.AsyncCallback - the method takes one IAsyncResult parameter (in our console app we use the DoSomeUsefulWorkWrapUp method).  This callback is passed into the BeginInvoke method after the original signature values (in our console app, just the secondsToSleep integer value).  This method is called when the method has completed its work.  The BeginInvoke also takes a final parameter that holds an object that contains state information that needs to be passed around.

The AsyncCallback method passed into the BeginInvoke will be called receiving an IAsyncResult object as its parameter.  This object is the key to Asynchronous method working - it is of type System.Runtime.Remoting.Messaging.AsyncResult but it holds all of the information for the calling method, exception and other useful information in its _replyMsg [((System.Runtime.Remoting.Messaging.AsyncResult)(ar))._replyMsg].  It holds the state information passed around by the user in the AsyncState.

Console App:

using System.Diagnostics;

using System.Runtime.Remoting.Messaging;

using System.Threading;

using System;

 

namespace BlogConsoleDemoApp

{

    class Program

    {

        private static int _counter = 0;

        static void Main()

        {

            const int SLEEP_DURATION_IN_SECONDS = 2;

            AsynchronousCall(SLEEP_DURATION_IN_SECONDS);

            Trace.WriteLine("****");

            SynchronousCall(SLEEP_DURATION_IN_SECONDS);

            DoSomeUsefulWork(15);

        }

 

        private static void AsynchronousCall(int SLEEP_DURATION_IN_SECONDS)

        {

            PrintTimestamp("before asynchronous call to method");

            DoSomeUsefulWorkDelegate delDoSomeUsefulWorkDelegate = DoSomeUsefulWorkWithAnException;

            AsyncCallback ac = DoSomeUsefulWorkWrapUp;

            // Call method asynchronously 6 times.

            for (int i = 0; i < 6; i++)

            {

                delDoSomeUsefulWorkDelegate.BeginInvoke(SLEEP_DURATION_IN_SECONDS, ac, "passed around state");

            }

            PrintTimestamp("after asynchronous call to method");

        }

 

        private static void SynchronousCall(int secondsToSleep)

        {

            PrintTimestamp("before synchronous call to method");

            DoSomeUsefulWork(secondsToSleep);

            PrintTimestamp("after synchronous call to method");

        }

 

        private static void DoSomeUsefulWork(int secondsToSleep)

        {

            // sleep

            Thread.Sleep(secondsToSleep*1000);

        }

 

        private static void DoSomeUsefulWorkWithAnException(int secondsToSleep)

        {

            if ((++_counter % 3) != 0)

            {

                // sleep

                PrintTimestamp("before call in work with exception method");

                Thread.Sleep(secondsToSleep*1000);

                PrintTimestamp("after call in work with exception method");

            }

            else

            {

                int workerThreads, completionPortThreads;

 

                ThreadPool.GetAvailableThreads(out workerThreads,

                                               out completionPortThreads);

 

                // build a message to log

                string message =

                    String.Format(@"Is Thread Pool Thread: {0}, Thread Id: {1} Available Worker Threads {2}",

                                  Thread.CurrentThread.IsThreadPoolThread,

                                  Thread.CurrentThread.GetHashCode(),

                                  workerThreads);

 

                throw new Exception("Exception thrown: " + message);

            }

 

        }

 

        public delegate void DoSomeUsefulWorkDelegate(int secondsToSleep);

 

        private static void DoSomeUsefulWorkWrapUp(IAsyncResult ar)

        {

            DoSomeUsefulWorkDelegate doSomeUsefulWorkDelegate = (DoSomeUsefulWorkDelegate)((AsyncResult)ar).AsyncDelegate;

            try

            {

                doSomeUsefulWorkDelegate.EndInvoke(ar);

            }

            catch (Exception ex)

            {

                PrintTimestamp(ex.Message);

            }

        }

 

        private static void PrintTimestamp(string message)

        {

            Trace.WriteLine(string.Format("Timestamp {0}: {1}", message, DateTime.Now.ToLongTimeString()));

        }

    }

}

Output:

Timestamp before asynchronous call to method: 2:02:52 AM

Timestamp before call in work with exception method: 2:02:52 AM

Timestamp after asynchronous call to method: 2:02:52 AM

****

Timestamp before synchronous call to method: 2:02:52 AM

Timestamp before call in work with exception method: 2:02:53 AM

Timestamp after synchronous call to method: 2:02:54 AM

Timestamp after call in work with exception method: 2:02:54 AM

Timestamp before call in work with exception method: 2:02:54 AM

Timestamp Exception thrown: Is Thread Pool Thread: True, Thread Id: 13 Available Worker Threads 247: 2:02:54 AM

Timestamp before call in work with exception method: 2:02:54 AM

Timestamp after call in work with exception method: 2:02:55 AM

Timestamp Exception thrown: Is Thread Pool Thread: True, Thread Id: 14 Available Worker Threads 246: 2:02:55 AM

Timestamp after call in work with exception method: 2:02:56 AM

Timestamp after call in work with exception method: 2:02:56 AM

 

That's it for this time - hope it helps.

Comments

No Comments