Chapter 6. Enhancing the Responsiveness of the Functional Program with Asynchronous Programming

Responsive applications are a must in today's programming approach. They can improve the performance of the application itself and make our application have a user-friendly interface. We need to asynchronously run the code execution process in our program to have a responsive application. To achieve this goal, in this chapter, we will discuss the following topics:

  • Using thread and thread pool to build a responsive application
  • Learning about asynchronous programming model patterns
  • Learning about task-based asynchronous patterns
  • Using the async and await keywords to build asynchronous programming
  • Applying an asynchronous method in a functional approach

Building a responsive application

The first time .NET Framework was announced, the flow of the program was executed sequentially. The drawback of this execution flow is that our application has to wait for the operation to finish before executing the next operation. It will freeze our application, and that will be an unpleasant user experience.

To minimize this problem, .NET Framework introduces thread, the smallest unit of execution, which can be scheduled independently by the OS. And the asynchronous programming means that you run a piece of code on a separate thread, freeing up the original thread and doing other things while the task is completed.

Running a program synchronously

Let's start our discussion by creating a program that will run all operations synchronously. The following is the code that demonstrates the synchronous operation that we can find in the SynchronousOperation.csproj project:

public partial class Program 
{ 
  public static void SynchronousProcess() 
  { 
    Stopwatch sw = Stopwatch.StartNew(); 
    Console.WriteLine( 
      "Start synchronous process now..."); 
    int iResult = RunSynchronousProcess(); 
    Console.WriteLine( 
      "The Result = {0}",iResult); 
    Console.WriteLine( 
      "Total Time = {0} second(s)!", 
      sw.ElapsedMilliseconds/1000); 
  } 
  public static int RunSynchronousProcess() 
  { 
    int iReturn = 0; 
    iReturn += LongProcess1(); 
    iReturn += LongProcess2(); 
    return iReturn; 
  } 
  public static int LongProcess1() 
  { 
    Thread.Sleep(5000); 
    return 5; 
  } 
  public static int LongProcess2() 
  { 
    Thread.Sleep(7000); 
    return 7; 
  } 
} 

As we can see in the preceding code, the RunSynchronousProcess() method executes two methods; they are the LongProcess1() and LongProcess2() methods. Now let's call the preceding RunSynchronousProcess() method, and we will get the following output on the console:

Running a program synchronously

These two methods, LongProcess1() and LongProcess2(), are independent, and each method takes a particular time to finish. Since it is executed synchronously, it takes 12 seconds to finish these two methods. The LongProcess1() method needs 5 seconds to finish, and the LongProcess2() method needs 7 seconds to finish.

Applying threads in the program

We can improve our previous code so that it can be the responsive program by refactoring some of the code and adding threads to the code. The refactored code would be as follows, which we can find in the ApplyingThreads.csproj project:

public partial class Program 
{ 
  public static void AsynchronousProcess() 
  { 
    Stopwatch sw = Stopwatch.StartNew(); 
    Console.WriteLine( 
      "Start asynchronous process now..."); 
    int iResult = RunAsynchronousProcess(); 
    Console.WriteLine( 
      "The Result = {0}", 
      iResult); 
    Console.WriteLine( 
      "Total Time = {0} second(s)!", 
      sw.ElapsedMilliseconds / 1000); 
  } 
  public static int RunAsynchronousProcess() 
  { 
    int iResult1 = 0; 
    // Creating thread for LongProcess1() 
    Thread thread = new Thread( 
      () => iResult1 = LongProcess1()); 
    // Starting the thread 
    thread.Start(); 
    // Running LongProcess2() 
    int iResult2 = LongProcess2(); 
    // Waiting for the thread to finish 
    thread.Join(); 
    // Return the the total result 
    return iResult1 + iResult2; 
  } 
  public static int LongProcess1() 
  { 
    Thread.Sleep(5000); 
    return 5; 
  } 
  public static int LongProcess2() 
  { 
    Thread.Sleep(7000); 
    return 7; 
  } 
} 

As we can see, we refactor the RunSynchronousProcess() method in the previous code into the RunAsynchronousProcess() method. And if we run the RunAsynchronousProcess() method, we will get the following output on the console:

Applying threads in the program

Compared to the RunSynchronousProcess() method, we now have a faster process in the RunAsynchronousProcess() method. We create a new thread that will run the LongProcess1() method. The thread will not run until it has started using the Start() method. Take a look at the following code snippet, in which we create and run the thread:

// Creating thread for LongProcess1() 
Thread thread = new Thread( 
  () => 
  iResult1 = LongProcess1()); 
// Starting the thread 
thread.Start(); 

After the thread is run, we can run the other operation, in this case, the LongProcess2() method. When this operation is done, we have to wait for the thread to be finished so that we use the Join() method from the thread instance. The following code snippet will explain this:

// Running LongProcess2() 
int iResult2 = LongProcess2(); 
// Waiting for the thread to finish 
thread.Join(); 

The Join() method will block the current thread until the other thread that's being executed is finished. After the other thread finishes, the Join() method will return, and then the current thread will be unblocked.

Creating threads using thread pool

Besides using the thread itself, we can also precreate some threads using the System.Threading.ThreadPool class. We use this class if we need to work with threads from the thread pool. When using thread pool, you are more likely to use only the QueueUserWorkItem() method. This method will add an execution request to the thread pool queue. If any threads are available in the thread pool, the request will execute right away. Let's take a look at the following code in order to demonstrate the use of thread pool, which we can find in the UsingThreadPool.csproj project:

public partial class Program 
{ 
  public static void ThreadPoolProcess() 
  { 
    Stopwatch sw = Stopwatch.StartNew(); 
    Console.WriteLine( 
      "Start ThreadPool process now..."); 
    int iResult = RunInThreadPool(); 
    Console.WriteLine("The Result = {0}", 
      iResult); 
    Console.WriteLine("Total Time = {0} second(s)!", 
      sw.ElapsedMilliseconds / 1000); 
  } 
  public static int RunInThreadPool() 
  { 
    int iResult1 = 0; 
    // Assignin work LongProcess1() to idle thread  
    // in the thread pool  
    ThreadPool.QueueUserWorkItem((t) => 
      iResult1 = LongProcess1()); 
    // Running LongProcess2() 
    int iResult2 = LongProcess2(); 
    // Waiting the thread to be finished 
    // then returning the result 
    return iResult1 + iResult2; 
  } 
    public static int LongProcess1() 
  { 
    Thread.Sleep(5000); 
    return 5; 
  } 
  public static int LongProcess2() 
  { 
    Thread.Sleep(7000); 
    return 7; 
  } 
} 

In the thread pool, we can invoke the QueueUserWorkItem() method to put a new work item in a queue, which is managed by the thread pool when we need to run the long running process instead of creating a new thread. There are three possibilities of how the work is treated when we send it to thread pool; they are as follows:

  • The thread has one or more available threads idle in the thread pool so that the work can be handled by the idle thread and run immediately.
  • No threads are available, but the MaxThreads property has not been reached yet so the thread pool will create a new thread, assign the work, and run the work immediately.
  • There is no available thread in the thread pool and the total number of threads in the thread pool has reached the MaxThreads. In this situation, the work item will wait in the queue for the first available thread.

Now, let's run the ThreadPoolProcess() method, and we will get the following output on the console:

Creating threads using thread pool

As we can see in the preceding screenshot, we get the same result with the similar process time we get when we apply the new thread that we discussed in the previous section.

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

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