Sunday, June 29, 2014

Asynchronous Programming in C# – Part 1:Basics of Thread programming


In these series of articles we are going to discuss on the following items:
  • Single Thread programming
  • Multi-Threading programming
  • Synchronous Programming
  • Asynchronous Programming
  • Advantages/Disadvantages of writing programming using Thread class
  • Advantages/Disadvantages of using Task Parallel Library classes
  • Deep dive into Asynchronous Programming using async and await modifiers

This article uses Visual Studio 2013 and .NET 4.5 version and C# programming language in order to explain the concepts. If you have .NET 3.5, you can still use the examples illustrated with System.Threading classes. If you have .NET 4 version, you should be good in using the examples illustrated with System.Threading classes and System.Threading.Tasks classes. If you have latest version of .NET (.NET 4.5+), you can also follow async and await modifiers examples illustrated in addition to the Threading and Tasks classes.

Convention followed in these articles: Since I am going to use mostly console applications to demonstrate the concepts, I would refer the "Foreground thread" as "Main Thread"  for convenience as it will be running in the main method of the program.

 

Windows OS and Threads:

Threads are the basic unit to which an operating system allocates processor time, and more than one thread can be executing code inside that process. You can find more from the MSDN http://msdn.microsoft.com/en-us/library/aa720724(v=vs.71).aspx.

System.Threading is a low level API for starting, stopping and joining threads.

Windows allows each thread to execute for a certain period. After the period ends, the thread is paused and Windows switches to another thread. This is called Context Switching.

There are two types of threads: Foreground Threads or Main Thread or UI thread which keeps application alive and Background Threads or Worker Threads which processes background work or long-time consuming tasks and reports back to the Foreground Threads.



Single Thread Programming -  All the code in your program is going to be executed on a single thread also called as Main thread or UI thread in case of UI programming.

image

  1. When you run your application, Windows operating system creates a Thread and it is given control to execute the entry method “Main”.
  2. All lines of code in the Main method gets executed on this thread.
  3. If there are any method calls, then the control goes to the called method and the code in those methods are still executed on the same thread. In the picture above, the main thread is “Thread1” and the called methods are “Method1() and Method2()”.
  4. Once the method execution is completed, then the control comes back to the Main method.
  5. Once all the code is executed in the Main method, then Thread is no more used.
  6. Then thread will be killed or destroyed by the operation system.

Here the all methods are executed on same thread and in sequential flow.

If your code is supposed to be intended to run in sequential and there are no I/O tasks involved or any heavy computational tasks involved, then this approach works fine. If there are any time consuming tasks are to be executed, then it will block your application and leaves it in an unusable state.

Simple program to demonstrate Single Threading:

using System;
using System.Threading;

namespace ThreadingTest
{
    class Program
    {
        /// <summary>
        /// Entry method
        /// </summary>
        /// <param name="args">Arguments to the method</param>
        static void Main(string[] args)
        {
            Console.WriteLine("Main thread {0}", Thread.CurrentThread.ManagedThreadId);
            ExecuteMethod1();
            Console.WriteLine("Main Method logic-1 executing....");
            ExecuteMethod2();
            Console.WriteLine("Main Method logic-2 executing....");
            Console.WriteLine("All code execution completed");
            Console.ReadLine();
        }

        /// <summary>
        /// Method to be executed
        /// </summary>
        static void ExecuteMethod1()
        {

            Console.WriteLine("Method name: ExecuteMethod1 and executing on Thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
        }

        /// <summary>
        /// Method to be executed
        /// </summary>
        static void ExecuteMethod2()
        {

            Console.WriteLine("Method name: ExecuteMethod2 and executing on Thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
        }
    }
}

Output:

image

All code is executed on Thread 8. And also observe that all the lines of code are executed in a sequential order.

Multi-Thread Programming – Code in your program is distributed among multiple threads for execution. Here multiple thread may execute code in synchronous manner or asynchronous based on your programming model.

 

 

image

  1. Windows OS creates a Thread (Main thread) to execute the Main method.
  2. Main thread executes the lines of code in the Main method.
  3. Main method creates a new Thread to execute method Method1().
  4. Main thread then executes other lines of code in the Main method.
  5. Main method creates another new Thread to execute method Method2().
  6. Main thread completes its execution and it does not wait for other two threads to complete their code execution.

Simple program to demonstrate Multi-threading:

using System;
using System.Threading;

namespace ThreadingTest
{
    class Program
    {
        /// <summary>
        /// Entry method
        /// </summary>
        /// <param name="args">Arguments to the method</param>
        static void Main(string[] args)
        {
            Console.WriteLine("Main thread {0}", Thread.CurrentThread.ManagedThreadId);

            //Create Thread to execute ExecuteMethod1
            Thread thread1 = new Thread(ExecuteMethod1);
            thread1.Start();

            Console.WriteLine("Main Method logic-1 executing....");

            //Create Thread to execute ExecuteMethod1
            Thread thread2 = new Thread(ExecuteMethod2);
            thread2.Start();

            Console.WriteLine("Main Method logic-2 executing....");
            Console.WriteLine("All code execution completed");
            Console.ReadLine();
        }

        /// <summary>
        /// Method to be executed
        /// </summary>
        static void ExecuteMethod1()
        {

            Console.WriteLine("Method name: ExecuteMethod1 and executing on Thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
        }

        /// <summary>
        /// Method to be executed
        /// </summary>
        static void ExecuteMethod2()
        {

            Console.WriteLine("Method name: ExecuteMethod2 and executing on Thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
        }
    }
}

Output:

image

Notice that Main method is running on Thread 8, ExecuteMethod1() is running on Thread 9 and ExecuteMethod2() is running on Thread 10. Also observe that Thread 8 is not waiting for Thread 9 and 10 to complete their execution. This is because, all these threads are running in independent fashion and there is no way to communicate for Thread 9 and 10 with Thread 8. This is one of the drawback if you are writing multi-threading code by directly creating Thread objects.

Synchronous Programming – All blocks of code are executed in sequential manner. If you have 4 blocks of code say A, B, C and D then the execution order will be A—>B—>C—>D to complete the execution. Imagine if multiple threads are given the job of executing each block then the other threads must wait for the current thread to complete its task. In this case, UI gets freeze as UI thread will be waiting for other threads to complete their tasks.

image

  1. Windows creates a Thread (Main Thread) to execute Main method.
  2. Main thread executes the lines of code in the Main method.
  3. Main method creates a new thread to execute Method1().
  4. Then Main thread is blocked to wait for the completion of execution of Method1().
  5. Control transfers to the next line of code in the Main method.
  6. Main method again creates another thread to execute method Method2() and waits for the thread to complete its execution.
  7. After the thread has completed its execution, then the control is transferred back to the next line of code and then Main thread completes execution.

Simple example to demonstrate the Multiple threads running in Synchronous manner:

using System;
using System.Threading;

namespace ThreadingTest
{
    class Program
    {
        /// <summary>
        /// Entry method
        /// </summary>
        /// <param name="args">Arguments to the method</param>
        static void Main(string[] args)
        {
            Console.WriteLine("Main thread {0}", Thread.CurrentThread.ManagedThreadId);

            //Create Thread to execute ExecuteMethod1
            Thread thread1 = new Thread(ExecuteMethod1);
            thread1.Start();
            thread1.Join();

            Console.WriteLine("Main Method logic-1 executing....");

            //Create Thread to execute ExecuteMethod1
            Thread thread2 = new Thread(ExecuteMethod2);
            thread2.Start();
            thread2.Join();

            Console.WriteLine("Main Method logic-2 executing....");
            Console.WriteLine("All code execution completed");
            Console.ReadLine();
        }

        /// <summary>
        /// Method to be executed
        /// </summary>
        static void ExecuteMethod1()
        {

            Console.WriteLine("Method name: ExecuteMethod1 and executing on Thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
        }

        /// <summary>
        /// Method to be executed
        /// </summary>
        static void ExecuteMethod2()
        {

            Console.WriteLine("Method name: ExecuteMethod2 and executing on Thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
        }
    }
}

 

Output:

image

Notice that Main method is running on thread 8. When the thread Thread 9 is created to execute the method ExecuteMethod1(), it blocked the Thread 8 to run and so first the ExecuteMethod1() code is executed. The the control is transferred to the next line of code in Main method. The Thread 10 is created to execute the method ExecuteMethod2() and the Thread 10 blocks the Thread 8. Once it is completed its execution, the the control goes to the next line of code. So, thread 8 has to wait for other threads to complete their execution so that all the code is executed in sequential order though the code is executed on multiple threads.


Asynchronous Programming – Each block of code will be running in parallel and reports to the Main thread or UI thread once they have completed their execution. UI thread or Main thread do not need to wait for all the threads to complete execution. This is especially useful when user wants to perform some other action on UI while some tasks are running behind the scenes.

image

  1. Windows creates a thread (Main Thread) to execute Main method.
  2. Main Thread executes the lines of code in the method Main().
  3. Main method creates a new Thread to execute Method1() and another one for executing method Method2().
  4. Main thread completes its code execution and then waits for the other two threads to complete their execution.

Simple program to demonstrate the Asynchronous programming using multiple threads:

using System;
using System.Threading;

namespace ThreadingTest
{
    class Program
    {
        /// <summary>
        /// Entry method
        /// </summary>
        /// <param name="args">Arguments to the method</param>
        static void Main(string[] args)
        {
            Console.WriteLine("Main thread {0}", Thread.CurrentThread.ManagedThreadId);

            //Create Thread to execute ExecuteMethod1
            Thread thread1 = new Thread(ExecuteMethod1);
            thread1.Start();

            Console.WriteLine("Main Method logic-1 executing....");

            //Create Thread to execute ExecuteMethod1
            Thread thread2 = new Thread(ExecuteMethod2);
            thread2.Start();

            Console.WriteLine("Main Method logic-2 executing....");

            thread1.Join();
            thread2.Join();
            Console.WriteLine("All code execution completed");
            Console.ReadLine();
        }

        /// <summary>
        /// Method to be executed
        /// </summary>
        static void ExecuteMethod1()
        {

            Console.WriteLine("Method name: ExecuteMethod1 and executing on Thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
        }

        /// <summary>
        /// Method to be executed
        /// </summary>
        static void ExecuteMethod2()
        {

            Console.WriteLine("Method name: ExecuteMethod2 and executing on Thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
        }
    }
}

Output:

image

In this program we have added Thread.Join() methods for both the threads 11 and 12 at the end of the Main method before the last Console.WriteLine() method is executed. These two methods blocks the main thread 10 to wait for the completion of their execution and then the control is returned back to the next line of code in Main method.


So, we have discussed single thread and multiple thread programming.  We often encounter situations where we need to run code in different threads in order to complete the tasks in parallel and still make UI available to do other things. Creating few threads programmatically is beneficial but if we create more number of threads it will be a costly affair.


Passing value from a thread to the executing method:


If you want to pass a value to an executing method from a thread, you need to declare the parameter type as “object” in the method and pass the value as a parameter of the start method of the thread. And in the method you need to cast the object to the desired type while using it. In the following example we are passing the loop counter value as a parameter to the method ExecuteMethod() and casting the object to string type while appending in the message text.
 

using System;
using System.Threading;

namespace ThreadingTest
{
    class Program
    {
        /// <summary>
        /// Entry method
        /// </summary>
        /// <param name="args">Arguments to the method</param>
        static void Main(string[] args)
        {
            Console.WriteLine("Main thread {0}", Thread.CurrentThread.ManagedThreadId);

            //Create Thread to execute ExecuteMethod and pass a value
            Thread thread1 = new Thread(ExecuteMethod);
            thread1.Start("I love Microsoft");
            Console.WriteLine("All code execution completed");
            Console.ReadLine();
        }

        /// <summary>
        /// Method to be executed
        /// </summary>
        static void ExecuteMethod(object message)
        {

            Console.WriteLine("Method name: ExecuteMethod1 and executing on Thread: {0} \nand the message is '{1}'",
                Thread.CurrentThread.ManagedThreadId,
                message.ToString());
        }

    }
}


Output:

image 

 

Points to ponder:


A thread is relatively a heavy weight thing. It at least takes some mega bytes of stack space by default. So imagine the cost if we create more number of threads. Definitely it is not advisable. So, create a Thread in your program only if it is necessary.

You can use Thread Pool class provided by .NET Framework to reuse the threads similar to the database connection pool works. So whenever you are using a thread for the execution of part of code, instead of getting the thread killed we can send it back to the pool so that it can be used again. But there is no mechanism where we can know when the operation is finished and what is the return value of the thread. You can achieve the mechanism by using BackgroundWorker class provided by System.ComponentModel where it has capabilities like to report back the progress of the work, completion of the work. But there is no direct way to do this using Thread class.

One more point to note is that if there are few set of operations that do not consume much time out of the whole operations, creation of Thread for executing those operations is a unnecessary burden to the operating system. All these light operations can be handled by a single thread. It is not advisable to programmatically handle these situations in the code through the creation of Threads. Microsoft has been continuously improving the built in classes for Asynchronous operations in each of the new version it is releasing.

In the next article we will discuss on System.Threading.Tasks provided in .NET version 4.0. It is a high level API for concurrent and asynchronous programming.

No comments:

Post a Comment