© Vaskaran Sarcar 2020
V. SarcarDesign Patterns in C#https://doi.org/10.1007/978-1-4842-6062-3_27

27. Patterns in Asynchronous Programming

Vaskaran Sarcar1 
(1)
Garia, Kolkata, West Bengal, India
 

You see many interesting patterns in asynchronous programming, which is tough and challenging but interesting. It is often referred to as asynchrony. The overall concept did not evolve in one day, it took time, and in C# 5.0, you got async and await keywords to make it easier. Before that, programmers implemented the concept with various techniques. Each technique has its pros and cons. The goal of this chapter to introduce you with different asynchronous programming patterns.

Overview

To begin, let’s discuss asynchronous programming. In simple terms, you take a code segment in your application and run it on a separate thread. What is the key benefit? The simple answer is that you can free the original thread and let it continue to do its remaining tasks, while in a separate thread, you can perform a different task. This mechanism helps you develop modern-day applications; for example, when you implement a highly responsive user interface, these concepts are very useful.

Points to Remember
Broadly you notice three different patterns in asynchronous programming which are as follows:
  • IAsyncResult Pattern : Alternatively, it is known as the Asynchronous Programming Model (APM). In this pattern, at the core, you see the IAsyncResult interface to support the asynchronous behavior. In a synchronous model, if you have a synchronous method called XXX(), in the asynchronous version, you see the BeginXXX() and EndXXX() methods for the corresponding synchronous method. For example, in synchronous version, if you have the Read() method to support read operation; in asynchronous programming, you normally have BeginRead() and EndRead() methods to support the corresponding read operation asynchronously. Using this concept, from demonstration 5 to demonstration 7, you see the BeginInvoke and EndInvoke methods. But this pattern is not recommended for upcoming and new development.

  • Event-based Asynchronous Pattern (EAP) : This pattern came with the .NET Framework 2.0. It is based on the event mechanism. Here you see the method name with the Async suffix, one or multiple events, and EventArg derived types. This pattern is still in use, but not recommended for new development.

  • Task-based Asynchronous Pattern (TAP) : It first appeared in.NET Framework 4; it is the recommended practice for asynchronous programming. In C#, you often see the async and await keywords in this pattern.

To keep the chapter short, I could have omitted the discussions on APM and EAP, but I discuss them in this chapter so that you understand legacy code. At the same time, you discover the pathway of the continuous development of asynchronous programming.

To understand asynchronous programming better, let’s start our discussion with its counterpart: synchronous programming. A synchronous approach is straightforward, and the code paths are easy to understand, but in this kind of programming, you need to wait to get the results from a particular segment of code, and until you cannot do anything fruitful. For example, when a segment of code tries to open a webpage that takes time to load, or when a segment of code is exercising a long-running algorithm, and so forth. In these cases, if you follow the synchronous approach, you need to sit idle. As a result, even if your computer is super fast and it has more computational power, you are not using its full potential, which is not a good idea. Therefore, to support modern-day demands and build highly responsive applications, the need for asynchronous programming is growing day by day. So, you benefit when you know different implementation patterns in this category.

Using Synchronous Approach

In demonstration 1, I execute a simple program, starting with a synchronous approach . Here there are two simple methods called ExecuteMethodOne() and ExecuteMethodTwo() . Inside the Main() method, I call these methods synchronously (i.e., I invoked ExecuteMethodOne() first and then ExecuteMethodTwo()). To focus on the key discussion, I made these methods very simple. I put simple sleep statements inside them to ensure that the jobs performed by these methods take a measurable amount of time to complete. Once you run the application and notice the output, you see that only after ExecuteMethodOne() finishes its execution, can ExecuteMethodTwo() begin its execution. In this case, the Main() method cannot complete until the methods complete their executions.

Note

Throughout this chapter, you see these methods with slight variations. I tried to maintain similar methods so that you can compare different techniques of asynchronous programming easily. For the simple demonstration purposes, in these examples, I assume ExecuteMethodOne() takes more time to finish because it’ll perform some lengthy operation. So, I forced a relatively long sleep inside it. On the contrary, I assume that ExecuteMethodTwo() performs a small task, so I placed a relatively short sleep inside it.

Demonstration 1

Here is the complete demonstration.
using System;
using System.Threading;
namespace SynchronousProgrammingExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***A Synchronous Program Demonstration.***");
            Console.WriteLine("ExecuteMethodTwo() needs to wait for ExecuteMethodOne() to finish first.");
            ExecuteMethodOne();
            ExecuteMethodTwo();
            Console.WriteLine("End Main().");
            Console.ReadKey();
        }
        // First Method
        private static void ExecuteMethodOne()
        {
            Console.WriteLine("MethodOne has started.");
            // Some big task
            Thread.Sleep(1000);
            Console.WriteLine("MethodOne has finished.");
        }
        // Second Method
        private static void ExecuteMethodTwo()
        {
            Console.WriteLine("MethodTwo has started.");
            // Some small task
            Thread.Sleep(100);
            Console.WriteLine("MethodTwo has finished.");
        }
    }
}

Output

Here is the output.
***A Synchronous Program Demonstration.***
ExecuteMethodTwo() needs to wait for ExecuteMethodOne() to finish first.
MethodOne has started.
MethodOne has finished.
MethodTwo has started.
MethodTwo has finished.
End Main().

Using Thread Class

If you look closely at the methods in demonstration 1, you find that those methods were not dependent on each other. If you can execute them in parallel, the response time of your application is improved, and you can reduce the overall execution time. So, let’s find some better approaches.

You can implement the concepts of multithreading in this case. Demonstration 2 is a simple solution using threads . It shows substituting the ExecuteMethodOne() method inside a new thread.

Demonstration 2

using System;
using System.Threading;
namespace UsingThreadClass
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Asynchronous Programming using Thread class.***");
            //ExecuteMethodOne();
            //Old approach.Creating a separate thread for the following //task(i.e. ExecuteMethodOne.)
            Thread newThread = new Thread(() =>
            {
                Console.WriteLine("MethodOne has started on a separate thread.");
                // Some big task
                Thread.Sleep(1000);
                Console.WriteLine("MethodOne has finished.");
            }
            );
            newThread.Start();
            /*
               Taking a small sleep to increase the probability of executing ExecuteMethodOne() before ExecuteMethodTwo().
             */
            Thread.Sleep(20);
            ExecuteMethodTwo();
            Console.WriteLine("End Main().");
            Console.ReadKey();
        }
        // Second Method
        private static void ExecuteMethodTwo()
        {
            Console.WriteLine("MethodTwo has started.");
            // Some small task
            Thread.Sleep(100);
            Console.WriteLine("MethodTwo has finished.");
        }
    }
}

Output

The following is a possible output.
***Asynchronous Programming using Thread class.***
MethodOne has started on a separate thread.
MethodTwo has started.
MethodTwo has finished.
End Main().
MethodOne has finished.

Analysis

Notice that although ExecuteMethodOne() started early, ExecuteMethodTwo() did not wait for ExecuteMethodOne() to finish its execution. Also, since ExecuteMethodTwo() is doing very little (sleep time is 100 milliseconds), it was able to finish before ExecuteMethodOne() finished its execution. Not only this, since the main thread was not blocked, it was able to continue its execution.

Q&A Session

27.1 Why do you put a sleep statement before the execution of Method2() inside Main?

Good catch. It was not necessary, but in some cases, you may notice that even though you try to start ExecuteMethodOne() to execute on a separate thread before ExecuteMethodTwo() in the current thread, it doesn’t happen. As a result, you may notice the following output.
***Asynchronous Programming using Thread class.***
MethodTwo has started.
MethodOne has started in a separate thread.
MethodTwo has finished.
End Main().
MethodOne has finished.

This simple sleep statement helps you increase the probability of starting ExecuteMethodOne() before ExecuteMethodTwo() in this example.

Using ThreadPool Class

Creating threads directly in a real-world application is normally discouraged. Some key reasons behind this are as follows.
  • Maintaining too many threads incur tough and costly operations.

  • A large amount of time is wasted due to context switching, instead of doing real work.

To avoid directly creating threads , C# gives you the facility to use the built-in ThreadPool class. With this class, you can use the existing threads, which can be reused to serve your purpose. The ThreadPool class is very effective in maintaining the optimal number of threads in your application. So, if needed, you can execute some of your tasks asynchronously using this facility.

ThreadPool is a static class that contains some static methods; some of them have an overloaded version too. For your quick reference, Figure 27-1 is a partial screenshot from Visual Studio IDE that shows the methods in the ThreadPool class.
../images/463942_2_En_27_Chapter/463942_2_En_27_Fig1_HTML.jpg
Figure 27-1

A screenshot of ThreadPool class from Visual Studio 2019 IDE

In this section, our focus is on the QueueUserWorkItem method. Figure 27-1 shows that this method has two overloaded versions. Now to know the details of this method, let’s expand the method description in Visual Studio. For example, once you expand the first overloaded version of this method, you notice the following.
//
// Summary:
//     Queues a method for execution. The method executes when a thread //     pool thread becomes available.
//
// Parameters:
//   callBack:
//     A System.Threading.WaitCallback that represents the method to be //     executed.
//
// Returns:
//     true if the method is successfully queued; System.NotSupportedException
//     is thrown if the work item could not be queued.
//
// Exceptions:
//   T:System.ArgumentNullException:
//     callBack is null.
//
//   T:System.NotSupportedException:
//     The common language runtime (CLR) is hosted, and the host does not //     support this action.
[SecuritySafeCritical]
public static bool QueueUserWorkItem(WaitCallback callBack);
If you further investigate the method parameter, you find that WaitCallBack is a delegate with the following description.
//
// Summary:
//     Represents a callback method to be executed by a thread pool thread.
//
// Parameters:
//   state:
//     An object containing information to be used by the callback method.
[ComVisible(true)]
public delegate void WaitCallback(object state);
The second overloaded version of QueueUserWorkItem can take an additional object parameter named state. It is as follows.
public static bool QueueUserWorkItem(WaitCallback callBack, object state);

It tells that using this overloaded version, you can pass some valuable data to your method through this parameter. In the upcoming demonstration, I use both overloaded versions, and that’s why, in the upcoming example, in addition to ExecuteMethodOne() and ExecuteMethodTwo() (which you saw in the previous demonstrations), I introduce another method called ExecuteMethodThree() in which I pass an object parameter.

People often use the words- arguments and parameter interchangeably. But an expert programmer is often particular about this. The variable(s) used in a method definition is called parameters of the method. For example, if you see a method definition inside a class something like the following:
public void Sum(int firstNumber,int secondNumber)
you say that the firstNumber and secondNumber are the parameters of the method Sum. Now assume you have an object of the class, say ob. So, when you invoke the method using the following line:
ob.Sum(1,2)

you say that 1 and 2 are the arguments that you’ve passed to the Sum method.

In short, you can say that we pass the arguments to a method, and these values are assigned to the method parameters. Following these definitions, I should say in my comments that I have passed 10 as an argument to ExecuteMethodThree. But for the sake of simplicity, often programmers do not emphasize on these terms too much, and you may see these terms are used interchangeably.

Demonstration 3

To use the QueueUserWorkItem method effectively, you need to use a method that matches a WaitCallBack delegate signature. In the following demonstration, I queue two methods in a ThreadPool. In demonstration 1 and demonstration 2, ExecuteMethodTwo() did not accept any parameter. So, if you want to use this method as it is and pass it to QueueUserWorkItem, you get the following compilation error.
No overload for 'ExecuteMethodTwo' matches delegate 'WaitCallback'
So, let’s modify the ExecuteMethodTwo() method with a dummy object parameter as follows. (I kept the comments for your reference.)
/*
The following method's signature should match
the delegate WaitCallback.It is as follows:
public delegate void WaitCallback(object state)
*/
private static void ExecuteMethodTwo(object state)
{
  Console.WriteLine("--MethodTwo has started.");
  // Some small task
  Thread.Sleep(100);
  Console.WriteLine("--MethodTwo has finished.");
}
Let’s now introduce another method named ExecuteMethodThree(...), which truly uses the parameter. This method is described as follows.
private static void ExecuteMethodThree(object number)
{
 Console.WriteLine("---MethodThree has started.");
 int upperLimit = (int)number;
 for (int i = 0; i < upperLimit; i++)
 {
  Console.WriteLine("---MethodThree prints 3.0{0}", i);
 }
 Thread.Sleep(100);
 Console.WriteLine("---MethodThree has finished.");
}
Now go through the following demonstration and corresponding output.
using System;
using System.Threading;
namespace UsingThreadPool
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Asynchronous Programming using ThreadPool class.***");
            // Using Threadpool
            // Not passing any argument to ExecuteMethodTwo
            ThreadPool.QueueUserWorkItem(new WaitCallback(ExecuteMethodTwo));
            /*
             Passing 10 as the argument to
             ExecuteMethodThree.
            */
            ThreadPool.QueueUserWorkItem(new WaitCallback(ExecuteMethodThree), 10);
            ExecuteMethodOne();
            Console.WriteLine("End Main().");
            Console.ReadKey();
        }
        private static void ExecuteMethodOne()
        {
            Console.WriteLine("-MethodOne has started.");
            // Some big task
            Thread.Sleep(1000);
            Console.WriteLine("-MethodOne has finished.");
        }
        /*
        The following method's signature should match
        the delegate WaitCallback.It is as follows:
        public delegate void WaitCallback(object state)
        */
        private static void ExecuteMethodTwo(object state)
        {
            Console.WriteLine("--MethodTwo has started.");
            // Some small task
            Thread.Sleep(100);
            Console.WriteLine("--MethodTwo has finished.");
        }
        /*
        The following method has a parameter.
        This method's signature also matches the WaitCallBack
        delegate signature.
        */
        private static void ExecuteMethodThree(object number)
        {
            Console.WriteLine("---MethodThree has started.");
            int upperLimit = (int)number;
            for (int i = 0; i < upperLimit; i++)
            {
                Console.WriteLine($"---MethodThree prints 3.0{i}");
            }
            Thread.Sleep(100);
            Console.WriteLine("---MethodThree has finished.");
        }
    }
}

Output

The following is a possible output.
***Asynchronous Programming using ThreadPool class.***
-MethodOne has started.
--MethodTwo has started.
---MethodThree has started.
---MethodThree prints 3.00
---MethodThree prints 3.01
---MethodThree prints 3.02
---MethodThree prints 3.03
---MethodThree prints 3.04
---MethodThree prints 3.05
---MethodThree prints 3.06
---MethodThree prints 3.07
---MethodThree prints 3.08
---MethodThree prints 3.09
--MethodTwo has finished.
---MethodThree has finished.
-MethodOne has finished.
End Main().

Q&A Session

27.2 Using the simple delegate instantiation technique, if I use the following first line instead of the second line, will the application compile and run?

ThreadPool.QueueUserWorkItem(ExecuteMethodTwo);

ThreadPool.QueueUserWorkItem(new WaitCallback(ExecuteMethodTwo));

Yes, but since you are learning to use the WaitCallback delegate now, I used the detailed way of instantiation to draw your special attention to it.

Using Lambda Expression with the ThreadPool Class

If you like lambda expressions , you can use it in a similar context. For example, in the previous demonstration, you can replace ExecuteMethodThree(...) using the lambda expression as follows.

// Using lambda Expression
// Here the method needs a parameter(input).
// Passing 10 as an argument to ExecuteMethodThree
ThreadPool.QueueUserWorkItem((number) =>
{
  Console.WriteLine("--MethodThree has started.");
  int upperLimit = (int)number;
  for (int i = 0; i < upperLimit; i++)
  {
   Console.WriteLine("---MethodThree prints 3.0{0}", i);
  }
  Thread.Sleep(100);
  Console.WriteLine("--MethodThree has finished.");
  }, 10
);
So, in the previous demonstration, you can comment out the following line and replace ExecuteMethodThree(...) with the lambda expression introduced earlier.
ThreadPool.QueueUserWorkItem(new WaitCallback(ExecuteMethodThree), 10);

If you execute the program again, you get a similar output. For your reference, I present the full implementation in demonstration 4.

Demonstration 4

using System;
using System.Threading;
namespace UsingThreadPoolWithLambdaExpression
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Asynchronous Programming Demonstration.***");
            Console.WriteLine("***Using ThreadPool with Lambda Expression.***");
            // Using Threadpool
            // Not passing any parameter for ExecuteMethodTwo
            ThreadPool.QueueUserWorkItem(ExecuteMethodTwo);
            // Using lambda Expression
            // Here the method needs a parameter(input).
            // Passing 10 as an argument to ExecuteMethodThree
            ThreadPool.QueueUserWorkItem((number) =>
            {
                Console.WriteLine("--MethodThree has started.");
                int upperLimit = (int)number;
                for (int i = 0; i < upperLimit; i++)
                {
                    Console.WriteLine("---MethodThree prints 3.0{0}", i);
                }
                Thread.Sleep(100);
                Console.WriteLine("--MethodThree has finished.");
            }, 10
          );
            ExecuteMethodOne();
            Console.WriteLine("End Main().");
            Console.ReadKey();
        }
        /// <summary>
        /// ExecuteMethodOne()
        /// </summary>
        private static void ExecuteMethodOne()
        {
            Console.WriteLine("-MethodOne has started.");
            // Some big task
            Thread.Sleep(1000);
            Console.WriteLine("-MethodOne has finished.");
        }
        /*
        The following method's signature should match
        the delegate WaitCallback.It is as follows:
        public delegate void WaitCallback(object state)
        */
        private static void ExecuteMethodTwo(Object state)
        {
            Console.WriteLine("--MethodTwo has started.");
            // Some small task
            Thread.Sleep(100);
            Console.WriteLine("--MethodTwo has finished.");
        }
    }
}

Output

The following is a possible output.
***Asynchronous Programming Demonstration.***
***Using ThreadPool with Lambda Expression.***
--MethodTwo has started.
-MethodOne has started.
--MethodThree has started.
---MethodThree prints 3.00
---MethodThree prints 3.01
---MethodThree prints 3.02
---MethodThree prints 3.03
---MethodThree prints 3.04
---MethodThree prints 3.05
---MethodThree prints 3.06
---MethodThree prints 3.07
---MethodThree prints 3.08
---MethodThree prints 3.09
--MethodTwo has finished.
--MethodThree has finished.
-MethodOne has finished.
End Main().
Note

This time, you saw lambda expressions with the ThreadPool class. In demonstration 2, you saw lambda expressions with the Thread class.

Using IAsyncResult Pattern

I mentioned that the IAsyncResult interface helps you implement asynchronous behavior. I also told you that in a synchronous model, if you have a synchronous method called XXX, in the asynchronous version, you see the BeginXXX and EndXXX methods for the corresponding synchronous method. Now you see these in detail.

Polling Using Asynchronous Delegates

In demonstration 3 and demonstration 4, you saw a built-in WaitCallBack delegate. In general, delegates have many different uses. In this section, you see another important usage. Let’s consider polling, which is a mechanism that repeatedly checks a condition. In our upcoming example, let’s check whether a delegate instance completes its task or not.

Demonstration 5

This time, I modify the ExecuteMethodOne(...) and ExecuteMethodTwo() methods slightly. These methods can print the thread IDs. Instead of blindly sleeping for 1000 milliseconds, this time, I allow ExecuteMethodOne(...) to accept an int parameter, which supplies sleep times.

As in previous cases, ExecuteMethodTwo() sleeps only for 100 milliseconds, but ExecuteMethodOne(...) takes more time to complete its task compared to ExecuteMethodTwo(). To make it happen, in this example, I pass 3000 milliseconds inside ExecuteMethodOne(...)as the method argument.

Let’s look at the important segment of the code. Now my ExecuteMethodOne is as follows:
// First Method
private static void ExecuteMethodOne(int sleepTimeInMilliSec)
{
  Console.WriteLine("MethodOne has started.");
  Console.WriteLine($"Inside ExecuteMethodOne(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
  // Some big task
  Thread.Sleep(sleepTimeInMilliSec);
  Console.WriteLine(" MethodOne has finished.");
}
To match the signature, I declare the delegate Method1Delegate as follows.
public delegate void Method1Delegate(int sleepTimeinMilliSec);
And later I instantiate it as follows.
Method1Delegate method1Del = ExecuteMethodOne;
Everything is straightforward so far. Now come to the most important line of the code, which is as follows.
IAsyncResult asyncResult = method1Del.BeginInvoke(3000, null, null);

Do you remember that in the context of a delegate, you can use the Invoke() method ? But that time your code followed a synchronous path. Now you are exploring asynchronous programming, and so you see the BeginInvoke and EndInvoke methods. When the C# compiler sees the delegate keyword, it supplies these methods for a dynamically generated class.

BeginInvoke method’s return type is IAsyncResult. If you hover your mouse on BeginInvoke or notice its structure, you see that although ExecuteMethodOne accepts only one parameter, the BeginInvoke method always takes two additional parameters: one of type AsyncCallback and one of type object. You see the discussion on them shortly. In this example, I used the first parameter only and passed 3000 milliseconds as ExecuteMethodOne’s argument. But for the last two parameters of BeginInvoke, I passed null values.

The returned result of BeginInvoke is important and I hold the result in an IAsyncResult object. The IAsyncResult has the following the read-only properties.
public interface IAsyncResult
{
 bool IsCompleted { get; }
 WaitHandle AsyncWaitHandle { get; }
 object AsyncState { get; }
 bool CompletedSynchronously { get; }
}
For now, my focus is on the isCompleted property. If you expand these definitions further, you see that isCompleted is defined as follows.
//
// Summary:
//     Gets a value that indicates whether the asynchronous  operation has //     completed.
//
// Returns:
//     true if the operation is complete; otherwise, false.
bool IsCompleted { get; }

So, it’s clear that you can use this property to verify whether the delegate has completed its work.

In the following example, I check whether the delegate in other thread completes its work. If the work is not completed, I print asterisks (*) in the console window and forcing the main thread to take a short sleep, which is why you see the following segment of code in this demonstration.
while (!asyncResult.IsCompleted)
{
    // Keep working in main thread
    Console.Write("*");
    Thread.Sleep(5);
}
Lastly, the EndInvoke method accepts an argument of type IAsyncResult. So, I passed asyncResult as an argument in this method. Now go through the complete demonstration.
using System;
using System.Threading;
namespace PollingDemoInDotNetFramework
{
    //WILL NOT WORK ON .NET CORE.
    //RUN THIS PROGRAM ON .NET FRAMEWORK.
    class Program
    {
        public delegate void Method1Delegate(int sleepTimeinMilliSec);
        static void Main(string[] args)
        {
            Console.WriteLine("***Polling Demo.Run it in .NET Framework.***");
            Console.WriteLine("Inside Main(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
            // Synchronous call
            //ExecuteMethodOne(3000);
            Method1Delegate method1Del = ExecuteMethodOne;
            IAsyncResult asyncResult = method1Del.BeginInvoke(3000, null, null);
            ExecuteMethodTwo();
            while (!asyncResult.IsCompleted)
            {
                // Keep working in main thread
                Console.Write("*");
                Thread.Sleep(5);
            }
            method1Del.EndInvoke(asyncResult);
            Console.ReadKey();
        }
        // First Method
        private static void ExecuteMethodOne(int sleepTimeInMilliSec)
        {
            Console.WriteLine("MethodOne has started.");
            Console.WriteLine($"Inside ExecuteMethodOne(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
            // Some big task
            Thread.Sleep(sleepTimeInMilliSec);
            Console.WriteLine(" MethodOne has finished.");
        }
        // Second Method
        private static void ExecuteMethodTwo()
        {
            Console.WriteLine("MethodTwo has started.");
            Console.WriteLine($"Inside ExecuteMethodTwo(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
            // Some small task
            Thread.Sleep(100);
            Console.WriteLine("MethodTwo has finished.");
        }
    }
}

Output

The following is a possible output.
***Polling Demo.Run it in .NET Framework.***
Inside Main(),Thread id 1 .
MethodTwo has started.
Inside ExecuteMethodTwo(),Thread id 1.
MethodOne has started.
Inside ExecuteMethodOne(),Thread id 3.
MethodTwo has finished.
*******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
MethodOne has finished.

Q&A Session

27.3 In a previous case, ExecuteMethodOne(...) takes only one parameter, and the BeginInvoke takes three parameters. So, can I simply say that if ExecuteMethodOne(...) accepts n number of parameters, then BeginInvoke has n+2 parameters?

Yes, the initial set of parameters is based on your methods, but for the last two parameters, one is of type AsyncCallback, and the final one is of type object.

Points to Remember
  • This type of example was run in .NET Framework 4.7.2. If you execute the program in .NET Core 3.0, you get this exception: System.PlatformNotSupportedException: 'Operation is not supported on this platform. One of the primary reasons for this is that async delegates implementations depend on remoting features that are not present in .NET Core. The detailed discussion on this can be found at https://github.com/dotnet/runtime/issues/16312.

  • If you do not want to examine and print the asterisks (*) in the console window, you can simply call the EndInvoke() method of the delegate type once your main thread completes its execution. The EndInvoke() itself waits until the delegate completes its work.

  • If you don’t explicitly examine whether the delegate finishes its execution or not, or you simply forget to call EndInvoke(), the thread of the delegate stops after the main thread dies. For instance, if you comment out the following segment of code from the prior example.
    //while (!asyncResult.IsCompleted)
    //{
    //    Keep working in main thread
    //    Console.Write("*");
    //    Thread.Sleep(5);
    //}
    //method1Del.EndInvoke(asyncResult);
    //Console.ReadKey();
And run the application again, you may NOT see the statement "MethodOne has finished."
  • BeginInvoke helps the calling thread get the result of the asynchronous method invocation at a later time by using EndInvoke.

Using AsyncWaitHandle of IAsyncResult

Did you notice WaitHandle AsyncWaitHandle { get; } inside IAsyncResult? It is important, and this time, I show you an alternative approach using this property. If you see it closely, you find that AsyncWaitHandle returns a WaitHandle, and it has the following description.
//
// Summary:
//     Gets a System.Threading.WaitHandle that is used to wait for an //     asynchronous operation to complete.
//
// Returns:
//     A System.Threading.WaitHandle that is used to wait for an //     asynchronous operation to complete.
WaitHandle AsyncWaitHandle { get; }

The Visual Studio IDE confirms that WaitHandle is an abstract class that waits for exclusive access to shared resources. Inside WaitHandle, you see WaitOne() method with five different overloaded versions, which are as follows.

public virtual bool WaitOne(int millisecondsTimeout);
public virtual bool WaitOne(int millisecondsTimeout, bool exitContext);
public virtual bool WaitOne(TimeSpan timeout);
public virtual bool WaitOne(TimeSpan timeout, bool exitContext);
public virtual bool WaitOne();
In the upcoming demonstration, I used the first overloaded version and provided an optional timeout value in milliseconds. If you expand the method, you see the following summary associated with it.
// Summary:
// Blocks the current thread until the current System.Threading.WaitHandle // receives a signal, using a 32-bit signed integer to specify the time // interval in milliseconds.
//(Some other details omitted)
public virtual bool WaitOne(int millisecondsTimeout);

So, it’s clear that by using WaitHandle, you can wait for a delegate thread to finish its work. In the following program, if the wait is successful, the control exits from the while loop. But if a timeout occurs, WaitOne() returns false, and the while loop continues and prints asterisks (*) in the console.

Demonstration 6

using System;
using System.Threading;
//RUN THIS PROGRAM ON .NET FRAMEWORK.
namespace UsingWaitHandleInDotNetFramework
{
    class Program
    {
        public delegate void Method1Delegate(int sleepTimeinMilliSec);
        static void Main(string[] args)
        {
            Console.WriteLine("***Polling and WaitHandle Demo.***");
            Console.WriteLine("Inside Main(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
            // Synchronous call
            //ExecuteMethodOne(3000);
            // Asynchrous call using a delegate
            Method1Delegate method1Del = ExecuteMethodOne;
            IAsyncResult asyncResult = method1Del.BeginInvoke(3000, null, null);
            ExecuteMethodTwo();
            while (true)
            {
                // Keep working in main thread
                Console.Write("*");
                /*
                 There are 5 different overload method for WaitOne().Following method blocks the current thread until the
                 current System.Threading.WaitHandle receives a signal,using a 32-bit signed integer to specify the time interval in milliseconds.
                */
                if (asyncResult.AsyncWaitHandle.WaitOne(10))
                {
                    Console.Write(" Result is available now.");
                    break;
                }
            }
            method1Del.EndInvoke(asyncResult);
            Console.WriteLine(" Exiting Main().");
            Console.ReadKey();
        }
        // First Method
        private static void ExecuteMethodOne(int sleepTimeInMilliSec)
        {
            Console.WriteLine("MethodOne has started.");
            // It will have a different thread id
            Console.WriteLine($"Inside ExecuteMethodOne(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
            // Some big task
            Thread.Sleep(sleepTimeInMilliSec);
            Console.WriteLine(" MethodOne has finished.");
        }
        // Second Method
        private static void ExecuteMethodTwo()
        {
            Console.WriteLine("MethodTwo has started.");
            Console.WriteLine($"Inside ExecuteMethodTwo(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
            // Some small task
            Thread.Sleep(100);
            Console.WriteLine("MethodTwo has finished.");
        }
    }
}

Output

Here is one possible output.
***Polling and WaitHandle Demo.***
Inside Main(),Thread id 1 .
MethodTwo has started.
Inside ExecuteMethodTwo(),Thread id 1.
MethodOne has started.
Inside ExecuteMethodOne(),Thread id 3.
MethodTwo has finished.
*******************************************************************************************************************************************************************************************************************************************************************
MethodOne has finished.
***
Result is available now.
Exiting Main().

Analysis

If you compare this demonstration with the previous one, here you wait for the asynchronous operation to complete differently. Instead of using IsCompleted property, this time, you used the AsyncWaitHandle property of IAsyncResult. I showed you both variations, which can be seen in different applications.

Using Asynchronous Callback

Revisit the BeginInvoke method , which was used in the previous two demonstrations. Let’s review how I used it.
// Asynchrous call using a delegate
Method1Delegate method1Del = ExecuteMethodOne;
IAsyncResult asyncResult = method1Del.BeginInvoke(3000, null, null);

This code segment shows that inside the BeginInvoke method, I passed two null arguments for the last two method parameters. If you hover your mouse over the line of these prior demonstrations, you notice that BeginInvoke is expecting an IAsyncCallback delegate as the second parameter and an object for the third parameter in this case.

Let’s investigate the IAsyncCallback delegate . Visual Studio IDE tells that this delegate is defined in System namespace, and it has the following description.
//
// Summary:
//     References a method to be called when a corresponding asynchronous //     operation completes.
//
// Parameters:
//   ar:
//     The result of the asynchronous operation.
  [ComVisible(true)]
  public delegate void AsyncCallback(IAsyncResult ar);
You can use a callback method to execute something useful (for example, some housekeeping works). The AsyncCallback delegate has a void return type, and it accepts an IAsyncResult parameter. So, let's define a method that can match this delegate signature and call this method once the Method1Del instance finishes its execution. Here is a sample method (let’s call it ExecuteCallbackMethod) that is used in an upcoming demonstration.
/*
It's a callback method.This method will be invoked
when Method1Delegate completes its work.
*/
private static void ExecuteCallbackMethod(IAsyncResult asyncResult)
{
 //if null you can throw some exception
    if (asyncResult != null)
    {
     Console.WriteLine(" CallbackMethod has started.");
     Console.WriteLine($"Inside ExecuteCallbackMethod(...), Thread id {Thread.CurrentThread.ManagedThreadId} .");
     // Do some housekeeping work/ clean-up operation
     Thread.Sleep(100);
     Console.WriteLine("CallbackMethod has finished.");
    }
   }

Demonstration 7

Now go through the complete implementation.
using System;
using System.Threading;
namespace UsingAsynchronousCallback
{
    class Program
    {
        public delegate void Method1Delegate(int sleepTimeinMilliSec);
        static void Main(string[] args)
        {
            Console.WriteLine("***Using Asynchronous Callback.***");
            Console.WriteLine("Inside Main(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
            // Asynchrous call using a delegate
            Method1Delegate method1Del = ExecuteMethodOne;
            IAsyncResult asyncResult = method1Del.BeginInvoke(3000, ExecuteCallbackMethod, null);
            ExecuteMethodTwo();
            while (!asyncResult.IsCompleted)
            {
                // Keep working in main thread
                Console.Write("*");
                Thread.Sleep(5);
            }
            method1Del.EndInvoke(asyncResult);
            Console.WriteLine("Exit Main().");
            Console.ReadKey();
        }
        // First Method
        private static void ExecuteMethodOne(int sleepTimeInMilliSec)
        {
            Console.WriteLine("MethodOne has started.");
            Console.WriteLine($"Inside ExecuteMethodOne(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
            // Some big task
            Thread.Sleep(sleepTimeInMilliSec);
            Console.WriteLine(" MethodOne has finished.");
        }
        // Second Method
        private static void ExecuteMethodTwo()
        {
            Console.WriteLine("MethodTwo has started.");
            Console.WriteLine($"Inside ExecuteMethodTwo(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
            // Some small task
            Thread.Sleep(100);
            Console.WriteLine("MethodTwo has finished.");
        }
        /*
         It's a callback method.This method will be invoked
         when Method1Delegate instance completes its work.
         */
        private static void ExecuteCallbackMethod(IAsyncResult asyncResult)
        {
            if (asyncResult != null)//if null you can throw some exception
            {
                Console.WriteLine(" CallbackMethod has started.");
                Console.WriteLine($"Inside ExecuteCallbackMethod(...),Thread id {Thread.CurrentThread.ManagedThreadId} .");
                // Do some housekeeping work/ clean-up operation
                Thread.Sleep(100);
                Console.WriteLine("CallbackMethod has finished.");
            }
        }
    }
}

Output

The following is a possible output.
***Using Asynchronous Callback.***
Inside Main(),Thread id 1 .
MethodTwo has started.
Inside ExecuteMethodTwo(),Thread id 1.
MethodOne has started.
Inside ExecuteMethodOne(),Thread id 3.
MethodTwo has finished.
**************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
MethodOne has finished.
CallbackMethod has started.
Inside ExecuteCallbackMethod(...),Thread id 3 .
Exit Main().
CallbackMethod has finished.

Analysis

The callback method started its work only after ExecuteMethodOne finished its execution. Also, note that the ExecuteMethodOne and ExecuteCallbackMethod thread IDs are the same. It is because the callback method was invoked from the thread in which ExecuteMethodOne was running.

Q&A Session

27.4 What is a callback method ?

Normally, it is a method that is invoked after a specific operation is completed. You often see this kind of method in asynchronous programming when you do not know the exact finishing time of an operation, but you want to start a new task once a certain task is done. For example, in the previous example, ExecuteCallbackMethod can perform some clean-up work if ExecuteMethodOne allocates some resources during its execution.

27.5 I see that the callback method was not invoked from the main thread. Is it expected?

Yes. In this example, ExecuteCallbackMethod is the callback method that can start its execution only after ExecuteMethodOne completes its work. So, it makes sense that you call ExecuteCallbackMethod from the same thread in which ExecuteMethodOne was running.

27.6 Can I use a lambda expression in this example?

Good catch. To get a similar output, in the previous demonstration, instead of creating a new ExecuteCallbackMethod method and using the following line,
IAsyncResult asyncResult = method1Del.BeginInvoke(3000, ExecuteCallbackMethod, null);
you could replace it using a lambda expression as follows.
IAsyncResult asyncResult = method1Del.BeginInvoke(3000,
 (result) =>
{
    if (result != null)//if null you can throw some exception
    {
        Console.WriteLine(" CallbackMethod has started.");
        Console.WriteLine($"Inside ExecuteCallbackMethod(),Thread id { Thread.CurrentThread.ManagedThreadId }.");
        // Do some housekeeping work/ clean-up operation
        Thread.Sleep(100);
        Console.WriteLine("CallbackMethod has finished.");
    }
 },
null);

27.7 I see that when you used the callback method inside the BeginInvoke method, instead of passing an object as the final parameter, you passed a null value. Is there any specific reason for this?

No, I did not use that parameter in these demonstrations. Since it is an object parameter, you can pass anything meaningful to you. When you use a callback method, you can pass the delegate instance itself. It can help your callback method to analyze the result of the asynchronous method.

But for simplicity, let’s modify the previous demonstration and pass a string message as the last argument inside BeginInvoke. Let’s assume now you are modifying the existing line of code
IAsyncResult asyncResult = method1Del.BeginInvoke(3000,ExecuteCallbackMethod, null);
with the following one.
IAsyncResult asyncResult = method1Del.BeginInvoke(3000, ExecuteCallbackMethod, "Method1Delegate, Thank you for using me." );
To accommodate this change, lets modify the ExecuteCallbackMethod() method too.The newly added lines are shown in bold.
private static void ExecuteCallbackMethod(IAsyncResult asyncResult)
{
   if (asyncResult != null)//if null you can throw some exception
    {
     Console.WriteLine(" CallbackMethod has started.");
     Console.WriteLine($"Inside ExecuteCallbackMethod(...),Thread id { Thread.CurrentThread.ManagedThreadId} .");
     // Do some housekeeping work/ clean-up operation
     Thread.Sleep(100);
     // For Q&A 27.7
     string msg = (string)asyncResult.AsyncState;
     Console.WriteLine($"Callback method says : ‘{msg}’");
     Console.WriteLine("CallbackMethod has finished.");
     }
  }
If you run the program again, this time you can see the following output which conforms the new string message:
***Using Asynchronous Callback.***
Inside Main(),Thread id 1 .
MethodTwo has started.
Inside ExecuteMethodTwo(),Thread id 1.
MethodOne has started.
Inside ExecuteMethodOne(),Thread id 3.
MethodTwo has finished.
********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
MethodOne has finished.
CallbackMethod has started.
Exit Main().
Inside ExecuteCallbackMethod(...),Thread id 3 .
Callback method says : `Method1Delegate, Thank you for using me.'
CallbackMethod has finished.
Points to Remember

You have seen the implementation of polling, wait handles, and asynchronous callbacks using delegates. This programming model can be seen in other places in .NET Framework also, for example BeginGetResponse, BeginGetRequestStream of HttpWebRequest class or BeginExecuteNonQuery(), BeginExecuteReader(), BeginExecuteXmlReader() of SqlCommand class. These methods have overloaded versions too.

Using Event-based Asynchronous Pattern

In this section, you see the usage of event-based asynchronous patterns, which are initially tough to understand. Based on the complexity of your application, this pattern can take various forms. Here are some key characteristics of this pattern.
  • In general, an asynchronous method can be a replica of its synchronous version, but when you call it, it starts on a separate thread and then return immediately. This mechanism allows you to call a thread to continue while the intended operations run in the background. Examples of these operations can be a long-running process such as loading a large image, downloading a large file, connecting, establishing a connection to a database, and so forth. Event-based asynchronous patterns are helpful in these contexts. For example, once the long-running download operation is completed, an event can be raised to notify the information. The subscribers of the event can act based on this notification immediately.

  • You can execute multiple methods simultaneously and receive a notification when each one completes.

  • Using this pattern, you take advantage of multithreading, but at the same time, you hide the overall complexity.

  • In the simplest case, your method name has an Async suffix to tell others that you are using an asynchronous version of the method. At the same time, you have a corresponding event with a Completed suffix. In an ideal case, you should have a corresponding cancel method, and it should support displaying the progress bar/report. The method that supports the cancel operation can also be named MethodNameAsyncCancel (or simply CancelAsync).

  • Components like SoundPlayer, PictureBox, WebClient, and BackgroundWorker are commonly known representatives of this pattern.

I made a simple application using WebClient. Let’s look at it.

Demonstration 8

At the beginning of the program, you see that I needed to include some specific namespaces. I used the comments to tell you about their importance in this demonstration.

In this example, I want to download a file into my local system. But instead of using a true URL from the Internet, I stored the source file in my local system. This gives two major benefits.
  • You do not need an Internet connection to run this application.

  • Since you’re not using the Internet connection, the download operation is relatively faster.

Now look at the following block of code before you see the complete example.
WebClient webClient = new WebClient();
// File location
Uri myLocation = new Uri(@"C:TestData estfile_original.txt");
// Target location for download
string targetLocation = @"C:TestDatadownloaded_file.txt";
webClient.DownloadFileAsync(myLocation, targetLocation);
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
So far, things are straightforward and simple. But I draw your attention to the following lines of code.
webClient.DownloadFileAsync(myLocation, targetLocation);
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
You can see that in the first line, I use a method defined in WebClient called DownloadFileAsync. In Visual Studio, the method description tells us the following.
// Summary:
//     Downloads, to a local file, the resource with the specified URI. This method does not block the calling thread.
//
// Parameters:
//   address:
//     The URI of the resource to download.
//
//   fileName:
//     The name of the file to be placed on the local computer.
//
// Exceptions:
//   T:System.ArgumentNullException:
//     The address parameter is null. -or- The fileName parameter is null.
//
//   T:System.Net.WebException:
//     The URI formed by combining System.Net.WebClient.BaseAddress and address is invalid.
//     -or- An error occurred while downloading the resource.
//
//   T:System.InvalidOperationException:
//     The local file specified by fileName is in use by another thread.
public void DownloadFileAsync(Uri address, string fileName);

When you use this method, the calling thread is not blocked. (Actually, DownloadFileAsync is the asynchronous version of the DownloadFile method, which is also defined in WebClient.)

Now we come to the next line of code.
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
Visual Studio describes DownloadFileCompleted event as follows.
/ Summary:
//     Occurs when an asynchronous file download operation completes.
public event AsyncCompletedEventHandler DownloadFileCompleted;
It further describes AsyncCompletedEventHandler as follows.
// Summary:
//     Represents the method that will handle the MethodNameCompleted event
//     of an asynchronous operation.
//
// Parameters:
//   sender:
//     The source of the event.
//
//   e:
//     An System.ComponentModel.AsyncCompletedEventArgs that contains the //     event data.
public delegate void AsyncCompletedEventHandler(object sender, AsyncCompletedEventArgs e);
You can subscribe to the DownloadFileCompleted event to show a notification that the download operation is finished. To do that, I used the following method.
private static void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
{
    Console.WriteLine("Successfully downloaded the file now.");
}
Note

The DownloadCompleted method matches the signature of AsyncCompletedEventHandler delegate.

I assume that you have mastered the concept of delegates and events before you run this application. So, you know that I could replace the line of code.
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
with the following line of code.
webClient.DownloadFileCompleted += DownloadCompleted;
But I like to keep the long version for better readability. Now go through the complete example and output.
using System;
// For AsyncCompletedEventHandler delegate
using System.ComponentModel;
using System.Net; // For WebClient
using System.Threading; // For Thread.Sleep() method
namespace UsingWebClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Event Based Asynchronous Program Demo.***");
            // Method1();
            #region The lenghty operation(download)
            Console.WriteLine("Starting a download operation.");
            WebClient webClient = new WebClient();
            // File location
            Uri myLocation = new Uri(@"C:TestDataOriginalFile.txt");
            // Target location for download
            string targetLocation = @"C:TestDataDownloadedFile.txt";
            webClient.DownloadFileAsync(myLocation, targetLocation);
            webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
            #endregion
            ExecuteMethodTwo();
            Console.WriteLine("End Main()...");
            Console.ReadKey();
        }
        // ExecuteMethodTwo
        private static void ExecuteMethodTwo()
        {
            Console.WriteLine("MethodTwo has started.");
            // Some very small task
            Thread.Sleep(10);
            Console.WriteLine("MethodTwo has finished.");
        }
        private static void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
        {
            Console.WriteLine("Successfully downloaded the file now.");
        }
    }
}

Output

The following is a possible output.
***Event Based Asynchronous Program Demo.***
Starting a download operation.
MethodTwo has started.
MethodTwo has finished.
End Main()...
Successfully downloaded the file now.

Analysis

You can see that the download operation started before ExecuteMethodTwo() starts its execution. Still, ExecuteMethodTwo() completed its job before the download operation completed. If you are interested in the content of Original.txt, here it is.
Dear Reader,
This is my test file.It is originally stored at C:TestData in my system.

You can test with a similar file and contents for a quick verification at your end.

Additional Note

You can make this example even better when you introduce a progress bar. In that case, you can use a Windows Form App to get built-in support for the progress bar. Let’s ignore ExecuteMethodTwo() for now, and focus on the asynchronous download operation solely. You can make a basic form, as shown in Figure 27-2, which contains three simple buttons and one progress bar. (You need to drag and drop these controls on your form first and name them as shown in Figure 27-2. I assume that you know these simple activities.)
../images/463942_2_En_27_Chapter/463942_2_En_27_Fig2_HTML.jpg
Figure 27-2

A simple UI application to demonstrate event-based asynchrony

The following segment of code is self-explanatory.
using System;
using System.ComponentModel;
using System.Net;
using System.Windows.Forms;
namespace UsingWebClentWithWinForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void StartDownload_Click(object sender, EventArgs e)
        {
         WebClient webClient = new WebClient();
         Uri myLocation = new Uri(@"C:TestData estfile_original.txt");
         string targetLocation = @"C:TestDatadownloaded_file.txt";
         webClient.DownloadFileAsync(myLocation, targetLocation);
         webClient.DownloadFileCompleted += new      AsyncCompletedEventHandler(DownloadCompleted);
         webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
         MessageBox.Show("Executed download operation.");
    }
    private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
    {
         MessageBox.Show("Successfully downloaded the file now.");
    }
    private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
         progressBar.Value = e.ProgressPercentage;
    }
    private void ResetButton_Click(object sender, EventArgs e)
    {
         progressBar.Value = 0;
    }
    private void ExitButton_Click(object sender, EventArgs e)
    {
        this.Close();
    }
    }
}
Note

You can download the complete code for this application from the Apress website.

Output

Once you click StartDownloadButton, you get the output shown in Figure 27-3 and Figure 27-4.
../images/463942_2_En_27_Chapter/463942_2_En_27_Fig3_HTML.jpg
Figure 27-3

A runtime screenshot of the UI application

Once you click the OK button, you see the message box shown in Figure 27-4.
../images/463942_2_En_27_Chapter/463942_2_En_27_Fig4_HTML.jpg
Figure 27-4

Another message box pops up when you click the OK button

Q&A Session

27.8 What are the pros and cons associated with an event-based asynchronous program?

Here are some common pros and cons associated with this approach.

Pros
  • You can invoke a long-running method and return immediately. When the method completes, you can get a notification that you can use effectively.

Cons
  • Since you have segregated code, it’s often difficult to understand, debug, and maintain.

  • A major problem occurs when you subscribe to an event but later forget to unsubscribe. This mistake can lead to memory leaks in your application, and the impact can be severe; for example, your system hangs or is unresponsive, and you need to reboot it often.

Understanding Tasks

To understand the task-based asynchronous pattern (TAP), first, you must know what a task is. A task is simply a unit of work that you want to perform. You can complete this work in the same thread or a different thread. Using tasks, you can have better control over the threads; for example, you can perform a continuation work once a particular task is finished. A parent task can create child tasks, so you can organize the hierarchy. This kind of hierarchy is important when you cascade your messages. Consider an example. In your application, once a parent task is canceled, the child tasks should be canceled too.

You can create tasks in different ways. In the following demonstration, I created three tasks in three different ways. The following segment of code has supporting comments.
#region Different ways to create and execute task
// Using constructor
Task taskOne = new Task(MyMethod);
taskOne.Start();
// Using task factory
TaskFactory taskFactory = new TaskFactory();
// StartNew Method creates and starts a task.
// It has different overloaded version.
Task taskTwo = taskFactory.StartNew(MyMethod);
// Using task factory via a task
Task taskThree = Task.Factory.StartNew(MyMethod);
#endregion
You can see that all three tasks (taskOne, taskTwo, taskThree) try to do a similar operation: they simply execute MyMethod(), which is described as follows.
private static void MyMethod()
{
    Console.WriteLine("Task.id={0} with Thread id {1} has started.", Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
    // Some task
    Thread.Sleep(100);
    Console.WriteLine("MyMethod for Task.id={0} and Thread id {1} is completed.", Task.CurrentId,  Thread.CurrentThread.ManagedThreadId);
    }
You can see that inside MyMethod() , to distinguish the tasks and threads, I printed their corresponding IDs in the console. Apart from this, I passed the method name as an argument inside the StartNew() method . This method has 16 overloaded versions (at the time of this writing), and I used the one that is defined as follows.
//
// Summary:
//     Creates and starts a task.
//
// Parameters:
//   action:
//     The action delegate to execute asynchronously.
//
// Returns:
//     The started task.
//
// Exceptions:
//   T:System.ArgumentNullException:
//     The action argument is null.
public Task StartNew(Action action);

Since MyMethod() matches the signature of the Action delegate in this case, there was no problem for me to use this method with StartNew.

Points to Remember
For your reference, let’s recollect the theory behind the Action delegate. The method summary of the following code:
    public delegate void Action();

says that it encapsulates a method that has no parameters and does not return a value.

In the upcoming example (demonstration 9), you see that MyMethod() doesn’t accept any argument, and its return type is void; this is why I could use the method name inside StartNew().

But it is important to note that in advanced programming, you frequently see the generic versions of Action delegates. I selected the following lines from my book Getting Started with Advanced C# (Apress, 2020):

Action delegates can take 1 to 16 input parameters but do not have a return type. The overloaded versions are as follows:
Action<in T>
Action<in T1,in T2>
Action<in T1,in T2, in T3>
....
Action<in T1, in T2, in T3,in T4, in T5, in T6,in T7,in T8,in T9,in T10,in T11,in T12,in T13,in T14,in T15,in T16>
For example, if you have a method called CalculateSumOfThreeInts that takes three int’s as input parameters and whose return type is void, as follows:
private static void CalculateSumOfThreeInts(int i1, int i2, int i3)
{
    int sum = i1 + i2 + i3;
    Console.WriteLine("Sum of {0},{1} and {2} is: {3}", i1, i2, i3, sum);
}
You can use an Action delegate to get the sum of three integers, as follows:
Action<int, int, int> sum = new Action<int, int, int>( CalculateSumOfThreeInts);
sum(10, 3, 7);
Otherwise, you can use the short form as follows:
Action<int, int, int> sum = CalculateSumOfThreeInts;
sum(10, 3, 7);

Demonstration 9

Now go through the complete demonstration and output.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace DifferentWaysToCreateTask
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Using different ways to create tasks.****");
            Console.WriteLine($"Inside Main().Thread ID:{Thread.CurrentThread.ManagedThreadId}");
            #region Different ways to create and execute task
            // Using constructor.
            Task taskOne = new Task(MyMethod);
            taskOne.Start();
            // Using task factory.
            TaskFactory taskFactory = new TaskFactory();
            // StartNew Method creates and starts a task.
            // It has different overloaded versions.
            Task taskTwo = taskFactory.StartNew(MyMethod);
            // Using task factory via a task.
            Task taskThree = Task.Factory.StartNew(MyMethod);
            #endregion
            Console.ReadKey();
        }
        private static void MyMethod()
        {
            Console.WriteLine($"Task.id={Task.CurrentId} with Thread id {Thread.CurrentThread.ManagedThreadId} has started.");
            Thread.Sleep(100);
            Console.WriteLine($"MyMethod for Task.id={Task.CurrentId} and Thread id {Thread.CurrentThread.ManagedThreadId} is completed.");
        }
    }
}

Output

The following is a possible output.
***Using different ways to create tasks.****
Inside Main().Thread ID:1
Task.id=3 with Thread id 6 has started.
Task.id=2 with Thread id 4 has started.
Task.id=1 with Thread id 5 has started.
MyMethod for Task.id=3 and Thread id 6 is completed.
MyMethod for Task.id=1 and Thread id 5 is completed.
MyMethod for Task.id=2 and Thread id 4 is completed.
Note

ManagedThreadId gets a unique identifier only for a particular managed thread. You may notice a different value when you run the application on your machine. So, you should not feel that since you have created n number of threads, you should see the thread ids between 1 to n only. There may be other threads running in the background.

Q&A Session

27.9  StartNew() can be used for the methods that match the Action delegate signature. Is this correct?

Not at all. I used it in one of the StartNew overloads that accepts a parameter, which is the name of a method that matches an Action delegate signature. But, there are other overloaded versions of StartNew; for example, consider the following, in which you see the Func delegates.
public Task<TResult> StartNew<[NullableAttribute(2)]TResult>
(Func<TResult> function, TaskCreationOptions creationOptions);
Or,
public Task<TResult> StartNew<[NullableAttribute(2)]TResult>
(Func<TResult> function, CancellationToken cancellationToken);

27.10 In a previous Q&A, I saw TaskCreationOptions . What does it mean?

It is an enum. You can set a task’s behavior using it. Here is the details of it.
public enum TaskCreationOptions
{
        None = 0,
        PreferFairness = 1,
        LongRunning = 2,
        AttachedToParent = 4,
        DenyChildAttach = 8,
        HideScheduler = 16,
        RunContinuationsAsynchronously = 64,
}

In an upcoming demonstration, you see another important enum called TaskContinuationOptions, which can also help you set a task behavior.

Using Task-based Asynchronous Pattern (TAP)

Task-based Asynchronous Pattern (TAP) came in C# 4.0. It is the foundation for async/await, which came in C# 5.0. TAP introduced the Task class and its generic variant Task<TResult>. Task is used when the return value of an asynchronous chunk of code is not a big concern. But when you do care about this return value, you should use the generic version, Task<TResult>. You have had an overview of Task. Let’s use this concept to implement a task-based asynchronous pattern using ExecuteMethodOne() and ExecuteMethodTwo() .

Demonstration 10

Here is a complete demonstration.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace UsingTAP
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Using Task-based Asynchronous Pattern.****");
            Console.WriteLine($"Inside Main().The thread ID:{Thread.CurrentThread.ManagedThreadId}");
            Task taskForMethod1 = new Task(ExecuteMethodOne);
            taskForMethod1.Start();
            ExecuteMethodTwo();
            Console.ReadKey();
        }
        private static void ExecuteMethodOne()
        {
            Console.WriteLine("Method1 has started.");
            Console.WriteLine($"Inside ExecuteMethodOne(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
            // Some big task
            Thread.Sleep(1000);
            Console.WriteLine("Method1 has completed its job now.");
        }
        private static void ExecuteMethodTwo()
        {
            Console.WriteLine("Method2 has started.");
            Console.WriteLine($"Inside ExecuteMethodTwo(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
            Thread.Sleep(100);
            Console.WriteLine("Method2 is completed.");
        }
    }
}

Output

The following is a possible output.
***Using Task-based Asynchronous Pattern.****
Inside Main().The thread ID:1
Method2 has started.
Inside ExecuteMethodTwo(),Thread id 1.
Method1 has started.
Inside ExecuteMethodOne(),Thread id 4.
Method2 is completed.
Method1 has completed its job now.

You have just seen a sample demo of a task-based asynchronous pattern. I did not care about the return value of ExecuteMethodOne(). But let’s say that you are interested in whether ExecuteMethodOne() executed successfully or not. For simplicity, I use a string message to indicate successful completion in the upcoming example. And this time, you see a generic variant of Task, which is Task<string> in this example. For lambda expression lovers, I modified ExecuteMethodOne() with a lambda expression in this example, and to fulfill the key requirement, I adjusted the return type.

In this example, I added another method called, ExecuteMethodThree() . For comparison, this method is initially commented out; the program is executed, and the output is analyzed. Later, I uncomment it and create a task hierarchy using the method. Once this is done, the program is executed again, and you notice that ExecuteMethodThree() runs when ExecuteMethodOne() completes its job. I kept the comments to help you understand.

Now go through demonstration 11.

Demonstration 11

Here is a complete demonstration.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TAPDemonstration2
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Using Task-based Asynchronous Pattern.Using lambda expression into it.****");
            Console.WriteLine("Inside Main().Thread ID:{0}", Thread.CurrentThread.ManagedThreadId);
            // Task taskForMethod1 = new Task(Method1);
            // taskForMethod1.Start();
            Task<string> taskForMethod1 = ExecuteMethodOne();
            /*
             Wait for task to complete.
             If you use Wait() method as follows, you'll not see the  asynchonous behavior.
             */
            // taskForMethod1.Wait();
            // Continue the task
            // The taskForMethod3 will continue once taskForMethod1 is // finished
            // Task taskForMethod3 = taskForMethod1.ContinueWith(ExecuteMethodThree, TaskContinuationOptions.OnlyOnRanToCompletion);
            ExecuteMethodTwo();
            Console.WriteLine($"Task for Method1 was a : {taskForMethod1.Result}");
            Console.ReadKey();
        }
        // Using lambda expression
        private static Task<string> ExecuteMethodOne()
        {
            return Task.Run(() =>
            {
                string result = "Failure";
                try
                {
                    Console.WriteLine("Method1 has started.");
                    Console.WriteLine($"Inside Method1(),Task.id={Task.CurrentId}");
                    Console.WriteLine($"Inside Method1(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
                    //Some big task
                    Thread.Sleep(1000);
                    Console.WriteLine("Method1 has completed its job now.");
                    result = "Success";
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception caught:{0}", ex.Message);
                }
                return result;
            }
            );
        }
        private static void ExecuteMethodTwo()
        {
            Console.WriteLine("Method2 has started.");
            Console.WriteLine($"Inside ExecuteMethodTwo(),Thread id {Thread.CurrentThread.ManagedThreadId}.");
            Thread.Sleep(100);
            Console.WriteLine("Method2 is completed.");
        }
        private static void ExecuteMethodThree(Task task)
        {
            Console.WriteLine("Method3 starts now.");
            Console.WriteLine($"Task.id is:{Task.CurrentId} with Thread id is:{Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(20);
            Console.WriteLine($"Method3 with Task.id {Task.CurrentId} and Thread id {Thread.CurrentThread.ManagedThreadId} is completed.");
        }
    }
}

Output

The following is a possible output.
***Using Task-based Asynchronous Pattern.Using lambda expression into it.****
Inside Main().Thread ID:1
Method2 has started.
Inside ExecuteMethodTwo(),Thread id 1.
Method1 has started.
Inside Method1(),Task.id=1
Inside Method1(),Thread id 4.
Method2 is completed.
Method1 has completed its job now.
Task for Method1 was a : Success

Analysis

Did you notice that this time, I did not use the Start() method for taskForMethod1? Instead, I used the Run() method from the Task class to execute Method1(). Why did I do that? Well, inside the Task class, Run is a static method. The method summary in Visual Studio states the following about this Run method: "Queues the specified work to run on the thread pool and returns a System.Threading.Tasks.Task`1 object that represents that work." At the time of writing, this method had eight overloaded versions, which are as follows.
public static Task Run(Action action);
public static Task Run(Action action, CancellationToken cancellationToken);
public static Task<TResult> Run<TResult>(Func<TResult> function);
public static Task<TResult> Run<TResult>(Func<TResult> function, CancellationToken cancellationToken);
public static Task Run(Func<Task> function);
public static Task Run(Func<Task> function, CancellationToken cancellationToken);
public static Task<TResult> Run<TResult>(Func<Task<TResult>> function);
public static Task<TResult> Run<TResult>(Func<Task<TResult>> function, CancellationToken cancellationToken);
Now check another important point in this example. If you uncomment the following line,
// Task taskForMethod3 = taskForMethod1.ContinueWith(ExecuteMethodThree, TaskContinuationOptions.OnlyOnRanToCompletion);
and run the application again, you get output similar to the following.
***Using Task-based Asynchronous Pattern.Using lambda expression into it.****
Inside Main().Thread ID:1
Method2 has started.
Inside ExecuteMethodTwo(),Thread id 1.
Method1 has started.
Inside Method1(),Task.id=1
Inside Method1(),Thread id 4.
Method2 is completed.
Method1 has completed its job now.
Task for Method1 was a : Success
Method3 starts now.
Task.id is:2 with Thread id is:5
Method3 with Task.id 2 and Thread id 5 is completed.
You can see the ContinueWith() method helps continue a task. You may also notice the following.
TaskContinuationOptions.OnlyOnRanToCompletion
It simply states that the task will continue when taskForMethod1 completes its job. Similarly, you can opt for other options using the enum TaskContinuationOptions , which has the following description.
public enum TaskContinuationOptions
{
    None = 0,
    PreferFairness = 1,
    LongRunning = 2,
    AttachedToParent = 4,
    DenyChildAttach = 8,
    HideScheduler = 16,
    LazyCancellation = 32,
    RunContinuationsAsynchronously = 64,
    NotOnRanToCompletion = 65536,
    NotOnFaulted = 131072,
    OnlyOnCanceled = 196608,
    NotOnCanceled = 262144,
    OnlyOnFaulted = 327680,
    OnlyOnRanToCompletion = 393216,
    ExecuteSynchronously = 524288
}

Q&A Session

27.11 Can I assign multiple tasks at a time?

Yes, you can. For example, in the previously modified example, if you have another method called ExecuteMethodFour with the following description.
private static void ExecuteMethodFour(Task task)
{
    Console.WriteLine("Method4 starts now.");
    Console.WriteLine($"Task.id is:{ Task.CurrentId } with Thread id is :{ Thread.CurrentThread.ManagedThreadId } ");
            Thread.Sleep(10);
    Console.WriteLine($"Method4 with Task.id { Task.CurrentId } and Thread id { Thread.CurrentThread.ManagedThreadId } is completed."); ,
}
You can write the following lines.
Task<string> taskForMethod1 = Method1();
Task taskForMethod3 = taskForMethod1.ContinueWith(ExecuteMethodThree, TaskContinuationOptions.OnlyOnRanToCompletion);
 taskForMethod3 = taskForMethod1.ContinueWith(ExecuteMethodFour, TaskContinuationOptions.OnlyOnRanToCompletion);

This means that once taskForMethod1 completes the task, you see the continuation work with taskForMethod3, which executes both ExecuteMethodThree and ExecuteMethodFour.

It is also important to note that a continuation work can have another continuation work. For example, if you want something like the following.
  • Once taskForMethod1 finishes, then to continue with taskForMethod3; and

  • Once taskForMethod3 finishes, then only to continue with taskForMethod4

you can write something similar to the following.
// Method1 starts
Task<string> taskForMethod1 = Method1();
// Task taskForMethod3 starts after taskForMethod1
Task taskForMethod3 = taskForMethod1.ContinueWith(ExecuteMethodThree,
TaskContinuationOptions.OnlyOnRanToCompletion);
// Task taskForMethod4 starts after taskForMethod3
Task taskForMethod4 = taskForMethod3.ContinueWith(ExecuteMethodFour, TaskContinuationOptions.OnlyOnRanToCompletion);

Using the async and await Keywords

The async and await keywords make the TAP pattern very flexible. Since the beginning of this chapter, I used two methods. The first method is a long-running method that takes more time to complete than the second method. In the upcoming examples, I continue the case studies with similar methods. For simplicity, let’s call them Method1() and Method2(), respectively.

Initially, I used a nonlambda version, but in the analysis section, I used the lambda expression variant of the code. First, let’s look at Method1() again.
private static void Method1()
{
    Console.WriteLine("Method1 has started.");
    Console.WriteLine("Inside Method1(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
    // Some big task
    Thread.Sleep(1000);
    Console.WriteLine("Method1 has completed its job now.");
}
When you use lambda expression and use the async/await pair, your code may look like the following.
// Using lambda expression
private static async Task Method1()
{
    await Task.Run(() =>
    {
        Console.WriteLine("Method1 has started.");
        Console.WriteLine("Inside Method1(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
        // Some big task
        Thread.Sleep(1000);
        Console.WriteLine("Method1 has completed its job now.");
    }
    );
}

Have you noticed an interesting fact? The method bodies of the synchronous version and the asynchronous version are very similar. But many of the earlier solutions to implement asynchronous programming were not like this. (They were complex too.)

So, what does await do? When you analyze the code, you find that once you get an await, the calling thread jumps out of the method and continue with something else.

In the upcoming demonstration, I used Task.Run, and it caused the asynchronous call to continue on a separate thread. It does not mean that the continuation work should always be done on a new thread, because sometimes you aren’t worried about different threads; for example, when your call is waiting to establish a connection over a network to download something.

Lastly, in the nonlambda version (demonstration 12), I used the following block of code.
private static async Task ExecuteTaskOne()
{
    await Task.Run(Method1);
}

And inside Main(), instead of calling Method1(), I used ExecuteTaskOne() to execute Method1() asynchronously. You can see that I passed the method name, Method1, inside the Run method. You can recognize that I used the shortest overloaded version of the Run method here. Since Method1 matches the signature of an Action delegate, you can pass this method name as an argument in the Run method of the Task class.

Demonstration 12

Here is the complete demonstration.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace UsingAsyncAwait
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Exploring task-based asynchronous pattern(TAP) using async and await.****");
            Console.WriteLine("Inside Main().Thread ID:{0}", Thread.CurrentThread.ManagedThreadId);
            /*
             This call is not awaited.So,the current method
             continues before the call is completed.
             i.e., following async call is not awaited.
             */
            ExecuteTaskOne();
            Method2();
            Console.ReadKey();
        }
        private static async Task ExecuteTaskOne()
        {
            await Task.Run(Method1);
        }
        private static void Method1()
        {
            Console.WriteLine("Method1() has started.");
            Console.WriteLine("Inside Method1(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
            // Some big task
            Thread.Sleep(1000);
            Console.WriteLine("Method1() has completed its job now.");
        }
        private static void Method2()
        {
            Console.WriteLine("Method2() has started.");
            Console.WriteLine("Inside Method2(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
            //Some small task
            Thread.Sleep(100);
            Console.WriteLine("Method2() is completed.");
        }
    }
}
Note

I recommend that you execute the task-based asynchronous programs in the latest editions of Visual Studio 2019 to avoid some misbehaviors, which were seen in older versions of Visual Studio.

Output

The following is a possible output.
***Exploring task-based asynchronous pattern(TAP) using async and await.****
Inside Main().Thread ID:1
Method1() has started.
Inside Method1(),Thread id 4 .
Method2() has started.
Inside Method2(),Thread id 1 .
Method2() is completed.
Method1() has completed its job now.

Analysis

In the previous output, you can see that Method1() was invoked earlier, but Method2()’s execution was not blocked due to that. Please note that this output may vary. So, in some cases, you may also see that Method2() starts before Method1(). So, if you want Method1() to start first, you can put a small Sleep() before the Method2() execution. You can see that Method2() ran inside the main thread, whereas Method1() executed in a different thread.

If you prefer to use lambda expressions, you could replace the following code segment
private static async Task ExecuteTaskOne()
{
        await Task.Run(Method1);
}
private static void Method1()
{
        Console.WriteLine("Method1() has started.");
        Console.WriteLine("Inside Method1(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
        // Some big task
        Thread.Sleep(1000);
        Console.WriteLine("Method1() has completed its job now.");
}
with this one.
// Using lambda expression
private static async Task ExecuteMethod1()
{
    await Task.Run(() =>
    {
           Console.WriteLine("Method1() has started.");
           Console.WriteLine("Inside Method1(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
           // Some big task
           Thread.Sleep(1000);
           Console.WriteLine("Method1() has completed its job now.");
        }
    );
}

Now in the previous demonstration, instead of calling ExecuteTaskOne(), you can directly call the ExecuteMethod1() method to get a similar output.

Note

In the previous example, you see a warning message for the following line: ExecuteMethod1(); which tells the following:

Warning CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

If you hover your mouse on this, you get two suggestions: One of these suggestion tells you to apply discard as follows.
_ = ExecuteMethod1(); // applying discard
Note

The discards have been supported since C# 7.0. These are temporary, dummy, and unused variables in an application. Since these variables may not be on allocated storage, they can reduce memory allocations. These variables can enhance better readability and maintainability. You use an underscore (_) to indicate a discard variable in your application.

But if you follow the second suggestion and insert await before the line, like in the following.
await ExecuteMethod1();
The compiler raises another error that states the following.
Error CS4033 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
To remove this error, you need to make the containing async method (i.e., now you start with the following line.
static async Task Main(string[] args)
After applying async/await pair, the Main() method may look like the following.
class Program
{
    // static void Main(string[] args)
    static async Task Main(string[] args)
    {
        Console.WriteLine("***Exploring task-based asynchronous pattern(TAP) using async and await.****");
        Console.WriteLine("Inside Main().Thread ID:{0}", Thread.CurrentThread.ManagedThreadId);
        await ExecuteMethod1();
        // remaining code

This overall discussion is made to remind you that you should apply async/await together and place them properly.

I finish the chapter with one final demonstration, and this time, I slightly modify the calling sequence of the application. Now I introduce another method called Method3(), which is similar to Method2(). This newly added method can be called from ExecuteTaskOne(), which has the following structure.
private static async Task ExecuteTaskOne()
{
        Console.WriteLine("Inside ExecuteTaskOne(), prior to await() call.");
        int value=await Task.Run(Method1);
        Console.WriteLine("ExecuteTaskOne(), after await() call.");
        // Method3 will be called if Method1 executes successfully
        if (value = = 0)
        {
             Method3();
        }
}
Take a look at the previous segment of code. It simply says that I want to grab the return value from Method1(), and based on that value, I decide whether I call Method3() or not. So, this time, Method1()’s return type is not void; instead, it is returning an int (0 for successful completion, otherwise -1), and this method is restructured with a try-catch block like the following.
private static int Method1()
{
    int flag = 0;
    try
    {
           Console.WriteLine("Method1() has started.");
           Console.WriteLine("Inside Method1(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
           // Some big task
           Thread.Sleep(1000);
           Console.WriteLine("Method1() has completed its job now.");
 }
 catch (Exception e)
 {
        Console.WriteLine("Caught Exception {0}", e);
        flag = -1;
 }
 return flag;
}

Now go through the following example.

Demonstration 13

Here is the complete demonstration.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncAwaitAlternateDemonstration
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Exploring task-based asynchronous pattern(TAP) using async and await.****");
            Console.WriteLine("***This is a modified example with three methods.***");
            Console.WriteLine("Inside Main().Thread ID:{0}", Thread.CurrentThread.ManagedThreadId);
            /*
             This call is not awaited.So,the current method
             continues before the call is completed.
             i.e., following async call is not awaited.
             */
            _ = ExecuteTaskOne();
            Method2();
            Console.ReadKey();
        }
        private static async Task ExecuteTaskOne()
        {
            Console.WriteLine("Inside ExecuteTaskOne(), prior to await() call.");
            int value = await Task.Run(Method1);
            Console.WriteLine("Inside ExecuteTaskOne(), after await() call.");
            /*
            Method3() will be called if Method1()
            executes successfully(i.e. if it returns 0)
            */
            if (value == 0)
            {
                Method3();
            }
        }
        private static int Method1()
        {
            int flag = 0;
            try
            {
                Console.WriteLine("Method1() has started.");
                Console.WriteLine("Inside Method1(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
                //Some big task
                Thread.Sleep(3000);
                Console.WriteLine("Method1() has completed its job now.");
            }
            catch (Exception e)
            {
                Console.WriteLine("Caught Exception {0}", e);
                flag = -1;
            }
            return flag;
        }
        private static void Method2()
        {
            Console.WriteLine("Method2() has started.");
            Console.WriteLine("Inside Method2(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(100);
            Console.WriteLine("Method2() is completed.");
        }
        private static void Method3()
        {
            Console.WriteLine("Method3() has started.");
            Console.WriteLine("Inside Method3(),Thread id {0} .", Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(100);
            Console.WriteLine("Method3() is completed.");
        }
    }
}

Output

The following is a possible output.
***Exploring task-based asynchronous pattern(TAP) using async and await.****
***This is a modified example with three methods.***
Inside Main().Thread ID:1
Inside ExecuteTaskOne(), prior to await() call.
Method1() has started.
Inside Method1(),Thread id 4 .
Method2() has started.
Inside Method2(),Thread id 1 .
Method2() is completed.
Method1() has completed its job now.
Inside ExecuteTaskOne(), after await() call.
Method3() has started.
Inside Method3(),Thread id 4 .
Method3() is completed.

Analysis

Look at the output closely. You can see that Method3() needed to wait for Method1()’s completion, but Method2() could finish its execution before Method1() ends its execution. Here Method3() can continue if the returned value from Method1() is 0 only (if there is any exception raised inside Method1(), I set the flag value to –1). So, this scenario is similar to the ContinueWith() method in demonstration 11.

Point to Note
In demonstration 13, notice the following line of code inside ExecuteTaskOne().
int value=await Task.Run(Method1);

It simply divides the code segment into two parts: prior call to await and post call to await. This syntax is like any synchronous call, but by using await (inside an async method), you apply a suspension point and use the power of asynchronous programming.

I finish this chapter with some interesting notes from Microsoft. They can be handy when you further explore async/await keywords. Remember the following points.
  • The await operator cannot be present in the body of a lock statement.

  • You may see multiple await operators inside the body of an async method. But if it is not there, this does not raise any compile-time error. Instead, you get a warning, and the method executes synchronously. So, you may notice the following warning in a similar context: Warning CS1998 This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

A big chapter! Hopefully, I was able to demystify the different patterns in asynchronous programming. Although the IAsyncResult pattern and event-based asynchrony are not recommended in the upcoming chapters, I discussed them in this chapter because they help you understand legacy code, they show you the evolution of asynchronous programming. You may find them useful in the future.

This is the end of my discussions on patterns. I hope that you enjoyed learning these patterns. Now you are ready to jump into the vast ocean of programming using various patterns. Let’s explore the remaining corner cases, which can’t be mastered without practice. So, keep coding.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.107.90