Sunday, 9 July 2017

Thread Pooling in C# with example

Introduction
In this article document we will try to a very important concept called thread pooling in c#. Kindly note : Before reading this article we recommend to read our previous article i.e What is Threading and Types of Threading using a simple example in C# to read this article See Here.
In our previous document we have seen how to create threading, we learned about threading, implemented threading and implemented types of threading. Here we will go into next step and learn ThreadPooling C#. Now in order to understand thread pooling we will first understand concept of thread pooling, thread object life cycle and benchmarks between Thread Pooling and Thread Class using a real time example.

What is Thread Pooling in C# ?

In order to understand thread pooling first we will understand about thread life cycle, what exactly happens when a create a thread of a multithreaded application.

Thread Life Cycle

When a framework receives request call i.e method or function it creates thread object when a thread object is created it gets allocated with some memory resources after that method or function is executed or task executed then after this execution finally garbage collections removes that object to free some memory.
So first request calls then thread object creates then memory resources allocated then task executed then finally goes to garbage collection to free some memory.
Friends..this is how a Thread Life Cycle is been executed on .NET framework.
If you look at the above thread life cycle you can notice that for every thread object request the same steps are repeated again and again in a multithreaded environment. These steps can consume lots of memory space and CPU usage and can make our application a bit slow. Now to over come these by saving our memory usage and cpu processes we can implement thread pooling in our project.
Definition : Thread pooling is a process of creating one or more threads or collection of threads during initialization of a multithreaded environment and then instead of going it to a garbage collection it directly adds it to thread pool for reusability og those threads for same task or new task.
First time when a thread object is been created with some memory allocation to perform some task instead of sending that object to a garbage collector we will add that object to a thread pool. We can do this for a one or more threads. So next time when we want to use any thread object instead of creating a new thread object we can directly use it from a thread pool for reusing it for same task or a new task.
So in simple words what will happen now is for every request call on .net framework it will go to the thread pool and checks for free available threads and from that pool it will use it for executing some task. An image snapshot as shown below.
Friends..! As you saw from above image that biggest advantage of using thread pool is that creation and memory allocation is done only once while creating a new thread and this saves lots of time, cpu usage and memory. Thread Pool leads to large performance gains.

How to implement thread pooling

In this step we will see how to implement thread pooling in the projects but since this is an article document (for simple understanding) so will demonstrate using console application.
Let's create a console application and import the threading system name-space. In the next step create a simple static function called "Run()" and add some coding to it. Here below sample code we have looped it to 27 times using for loop.
01class Program
02 {
03     static void Main(string[] args)
04     {
05          
06     }
07 
08     static void Run(object callback)
09     {
10         for (int i = 0; i <= 27; i++)
11         {
12             Console.WriteLine("Number : " + i);
13             Thread.Sleep(240);
14         }
15     }
16 }
Now rather than creating a thread object and adding it to a function "Run" we will create a thread pool i.e ThreadPool.QueueUserWorkItem which Queues method for execution and the method executes when a thread thread becomes available to execute.
ThreadPool.QueueUserWorkItem takes overloaded waitcallback function we create a object waitcallback and inside the waitcallback we will pass our custom function "Run()" since waitcallback needs a call back object for call backs we have added that call-back object to our method "Run(object callback)".
The complete source code is shown below.
01class Program
02   {
03       static void Main(string[] args)
04       {
05           ThreadPool.QueueUserWorkItem(new WaitCallback(Run));
06           Console.ReadLine();
07       }
08 
09       static void Run(object callback)
10       {
11           for (int i = 0; i <= 27; i++)
12           {
13               Console.WriteLine("Number : " + i);
14               Thread.Sleep(240);
15           }
16       }
17   }
Output :

Performance benchmarks between Thread Object and Thread Pool using real time example

Now to perform performance benchmarks between Thread Object and Thread Pool we will first go ahead and create a console application and import threading namespace.
In the next step let's create static empty function called "Run()" with input as object callback as shown in below snippet of code.
01class Program
02{
03    static void Main(string[] args)
04    {
05        
06    }
07 
08    
09    static void Run(object callback)
10    {
11        
12    }  
13}
In next further step let's create a two funtions called as "RunWithThreadPool" and "RunWithoutThreadPool". Inside this functions we will first create loop for 10 times.
01static void RunWithThreadPool()
02  {
03      for (int i = 0; i <= 10; i++)
04      {
05          
06      }
07  }
08 
09  static void RunWithOutThreadPool()
10  {
11      for (int i = 0; i <= 10; i++)
12      {
13           
14      }
15  }
Now inside the for-loop of both functions let's add thread pool method in "RunWithThreadPool" function and in "RunWithOutThreadPool" function let's create a thread object and both of these functions i.e. Thread Pool and Thread object will call the same function called "Run()".
01static void RunWithThreadPool()
02{
03    for (int i = 0; i <= 10; i++)
04    {
05        ThreadPool.QueueUserWorkItem(new WaitCallback(Run));
06    }
07}
08 
09static void RunWithOutThreadPool()
10{
11    for (int i = 0; i <= 10; i++)
12    {
13        Thread obj = new Thread(Run);
14        obj.Start();
15    }
16}
In the next step let's call both of these functions in the main method as shown in below snippet of code.
1static void Main(string[] args)
2{
3 
4    RunWithThreadPool();
5    RunWithOutThreadPool();
6 
7}
Before executing this code let's add stopwatch to calculate time consumption of a each method and to ensure not to consider the compile time let's call both of these methods once or twice before calling the stopwatch as shown in below sample code.
01static void Main(string[] args)
02{
03 
04//You call these code once or twice
05    RunWithThreadPool();
06    RunWithOutThreadPool();
07 
08 
09    Stopwatch mywatch = new Stopwatch();
10 
11    Console.WriteLine("Run with thread pool");
12 
13    mywatch.Start();
14    RunWithThreadPool();
15    mywatch.Stop();
16 
17    Console.WriteLine("Time consumed with thread pool is : "+mywatch.ElapsedTicks.ToString());
18    mywatch.Reset();
19 
20 
21    Console.WriteLine("Run without thread pool");
22 
23    mywatch.Start();
24    RunWithOutThreadPool();
25    mywatch.Stop();
26 
27    Console.WriteLine("Time consumed with out thread pool is : " + mywatch.ElapsedTicks.ToString());
28}
Comple source code is shown below :
01using System;
02using System.Collections.Generic;
03using System.Linq;
04using System.Text;
05using System.Threading;
06using System.Diagnostics;
07 
08namespace ThreadPooling
09{
10    class Program
11    {
12        static void Main(string[] args)
13        {
14 
15            RunWithThreadPool();
16            RunWithOutThreadPool();
17 
18 
19            Stopwatch mywatch = new Stopwatch();
20 
21            Console.WriteLine("Run with thread pool");
22 
23            mywatch.Start();
24            RunWithThreadPool();
25            mywatch.Stop();
26 
27            Console.WriteLine("Time consumed with thread pool is : "+mywatch.ElapsedTicks.ToString());
28            mywatch.Reset();
29 
30 
31            Console.WriteLine("Run without thread pool");
32 
33            mywatch.Start();
34            RunWithOutThreadPool();
35            mywatch.Stop();
36 
37            Console.WriteLine("Time consumed with out thread pool is : " + mywatch.ElapsedTicks.ToString());
38        }
39 
40        static void RunWithThreadPool()
41        {
42            for (int i = 0; i <= 10; i++)
43            {
44                ThreadPool.QueueUserWorkItem(new WaitCallback(Run));
45            }
46        }
47 
48        static void RunWithOutThreadPool()
49        {
50            for (int i = 0; i <= 10; i++)
51            {
52                Thread obj = new Thread(Run);
53                obj.Start();
54            }
55        }
56 
57        static void Run(object callback)
58        {
59            
60        }
61 
62         
63    }
64}
Final step is to run this above to check the performance benchmarks between thread pool and thread object.
Display output is shown below.
Conclusion : As you see from display output. Time consumed with thread pool to execute any function it took 25664 ElapsedTicks and Time consumed without thread pool i.e. using thread object to execute any function it took 2785520 ElapsedTicks. So our performance benchmarks says that thread pool gives better performance than thread class object. So if you are looking to implement threading in your application use thread pooling over thread class. But if priority is not about the performance then better use thread class to keep the code simple.

No comments:

Post a Comment