Sunday, September 15, 2013

Example for a responsive console application with async/await vs threads

There are mainly two reasons for using asynchronous programming:
  1. Utilize your CPU cores by parallelizing CPU intensive work.
  2. Keep your application responsive while long running activities are in progress. These activities may be CPU intensive work or are waiting for a response from the web, lan or database (IO bound work)   
Responsiveness is not only required for applications with an user interface but also for background services and applications. Background services should answer additional request even when they are currently busy with something else. On a user interface a user wants to at least be able to cancel a long running activity.

The following simple example demonstrate asynchronous programming with .NET 4.5 and async/await with an console application. The two long running activities (DoAction1() and DoAction2()) with CPU bound work are executed in parallel. The user can exit the application even before the computation and printing of the result has finished:
class Program
{
 static void Main()
 {
     var program = new Program();

     program.PrintResultAsync();

     Console.WriteLine("Press <return> to exit ...");
     Console.ReadLine();
 }

 public int DoAction1()
 {
     int result = 0;
     for (int i = 0; i < 5; i++)
     {
       Console.WriteLine("Action1 "+ result);
       Thread.Sleep(250);
       result++;
     }
     return result;
 }

 public int DoAction2()
 {
     int result = 0;
     for (int i = 0; i < 5; i++)
     {
       Console.WriteLine("Action2 " + result);
       Thread.Sleep(250);
       result--;
     }
     return result;
 }


 public async void PrintResultAsync()
 {
     Tuple<int, int> r = await ComputeResultAsync();
     Console.WriteLine("Result " + r.Item1 + " + " + r.Item2 + " = " + (r.Item1 + r.Item2));
 }

 private async Task<Tuple<int, int>> ComputeResultAsync()
 {
     var t1 = Task<int>.Factory.StartNew(this.DoAction1);
     var t2 = Task<int>.Factory.StartNew(this.DoAction2);
     return new Tuple<int, int>(await t1, await t2);
 }
}

The application has the output on the console below:
We can compare the previous async/await implementation with an equal implementation using plain threads. The thread based implementation is much longer and requires more overhead like additional member variables:
class Program
{
 static void Main()
 {
     var program = new Program();

     program.PrintResultWithThreads();

     Console.WriteLine("Press <return> to exit ...");
     Console.ReadLine();
 }

 public int DoAction1()
 {
     int result = 0;
     for (int i = 0; i < 5; i++)
     {
       Console.WriteLine("Action1 "+ result);
       Thread.Sleep(250);
       result++;
     }
     return result;
 }

 public int DoAction2()
 {
     int result = 0;
     for (int i = 0; i < 5; i++)
     {
       Console.WriteLine("Action2 " + result);
       Thread.Sleep(250);
       result--;
     }
     return result;
 }

 private int r1;
 private int r2;
 private ManualResetEvent e1 = new ManualResetEvent(false);
 private ManualResetEvent e2 = new ManualResetEvent(false);
 private WaitHandle[] manualEvents;

 public void PrintResultWithThreads()
 {
     var printThread = new Thread(
       () =>
       {
         e1 = new ManualResetEvent(false);
         e2 = new ManualResetEvent(false);
         manualEvents = new WaitHandle[] { e1, e2 };
         ComputeResultsWithThreads();
         WaitHandle.WaitAll(manualEvents);
         Console.WriteLine("Result: " + r1 + " + " + r2 + " = " + (r1 + r2));
       });
     printThread.IsBackground = true;
     printThread.Start();
 }

 private void ComputeResultsWithThreads()
 {
     var t1 = new Thread(
       () =>
       {
         this.r1 = this.DoAction1();
         this.e1.Set();
       });
     t1.IsBackground = true;
     t1.Start();
     var t2 = new Thread(
       () =>
       {
         this.r2 = this.DoAction2();
         this.e2.Set();
       });
     t2.IsBackground = true;
     t2.Start();
 }
}

No comments: