Thread Pool In .NET Core And C#

A thread pool is a pool of worker threads that is available on demand as needed. The code examples in this article show how to use the thread pool in .NET Core using C#.

Creation and destruction of new threads comes with a cost and it affects an application’s performance. Threads can also be blocked or go into sleep or other unresolved states. If your app doesn’t distribute the workload properly, worker threads may spend the majority of their time sleeping. This is where thread pool comes in handy.
 
A thread pool is a pool of worker threads that have already been created and are available for apps to use them as needed. Once thread pool threads finish executing their tasks, they go back to the pool.
 
.NET provides a managed thread pool via the ThreadPool class that is managed by the system. As a developer, we don’t need to deal with the thread management overhead. For any short background tasks, the managed thread pool is a better choice than creating and managing your own threads. Thread pool threads are good for background processes only and not recommended for foreground threads. There is only one thread pool per process.
 
Use of thread pool is not recommended when,
  • You need to prioritize a thread.
  • Thread is a foreground thread.
  • You have tasks that cause the thread to block for long periods of time. The thread pool has a maximum number of threads, so a large number of blocked thread pool threads might prevent tasks from starting.
  • You need to place threads into a single-threaded apartment. All ThreadPool threads are in the multithreaded apartment.
  • You need to have a stable identity associated with the thread, or to dedicate a thread to a task.
If you're new to threading, start with Threading in C# and .NET Core.
 
ThreadPool
 
The ThreadPool class has several static methods including the QueueUserWorkItem that is responsible for calling a thread pool worker thread when it is available. If no worker thread is available in the thread pool, it waits until the thread becomes available.
 
The QueueWorkItem method takes a procedure that executes in the background.
 
ThreadPool.QueueUserWorkItem(BackgroundTask);

Here is a complete example of how to call a worker thread from thread pool to execute a method in the background.
  1. using System;  
  2. using System.Threading;  
  3.   
  4. class ThreadPoolSample  
  5. {  
  6.     // Background task   
  7.     static void BackgroundTask(Object stateInfo)  
  8.     {  
  9.         Console.WriteLine("Hello! I'm a worker from ThreadPool");  
  10.         Thread.Sleep(1000);          
  11.     }  
  12.   
  13.     static void Main(string[] args)  
  14.     {  
  15.         // Use ThreadPool for a worker thread        
  16.         ThreadPool.QueueUserWorkItem(BackgroundTask);  
  17.         Console.WriteLine("Main thread does some work, then sleeps.");  
  18.         Thread.Sleep(500);  
  19.         Console.WriteLine("Main thread exits.");  
  20.         Console.ReadKey();  
  21.     }  
  22. }  
You can also pass values to a background method via the QueueWorkItem method. The second parameter of the method is an object that can be any object you would like to pass to your background procedure.
 
Let’s assume we have a Person class with the following members.
  1. // Create a Person class  
  2. public class Person  
  3. {  
  4.     public string Name { getset; }  
  5.     public int Age { getset; }  
  6.     public string Sex { getset; }  
  7.   
  8.     public Person(string name, int age, string sex)  
  9.     {  
  10.         this.Name = name;  
  11.         this.Age = age;  
  12.         this.Sex = sex;  
  13.     }  
  14. }  
We can create an object of Person type and pass it to the QueueUserWorkItem method.
  1. // Create an object and pass it to ThreadPool worker thread  
  2. Person p = new Person("Mahesh Chand", 40, "Male");  
  3. ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  
  4. And in the worker method, we can extract values from the object and use it. In the following example, I read back the Person.Name and displays on the console.   
  5. static void BackgroundTaskWithObject(Object stateInfo)  
  6. {  
  7.     Person data = (Person)stateInfo;          
  8.     Console.WriteLine($"Hi {data.Name} from ThreadPool.");  
  9.     Thread.Sleep(1000);  
  10. }  
The complete code is listed in the following code.
  1. using System;  
  2. using System.Threading;  
  3.   
  4. class ThreadPoolSample  
  5. {  
  6.     // Background task   
  7.     static void BackgroundTask(Object stateInfo)  
  8.     {  
  9.         Console.WriteLine("Hello! I'm a worker from ThreadPool");  
  10.         Thread.Sleep(1000);          
  11.     }  
  12.   
  13.     static void BackgroundTaskWithObject(Object stateInfo)  
  14.     {  
  15.         Person data = (Person)stateInfo;          
  16.         Console.WriteLine($"Hi {data.Name} from ThreadPool.");  
  17.         Thread.Sleep(1000);  
  18.     }  
  19.     static void Main(string[] args)  
  20.     {  
  21.         // Create an object and pass it to ThreadPool worker thread  
  22.         Person p = new Person("Mahesh Chand", 40, "Male");  
  23.         ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  
  24.   
  25.         Console.ReadKey();  
  26.     }  
  27.   
  28.         // Create a Person class  
  29.         public class Person  
  30.         {  
  31.             public string Name { getset; }  
  32.             public int Age { getset; }  
  33.             public string Sex { getset; }  
  34.   
  35.             public Person(string name, int age, string sex)  
  36.             {  
  37.                 this.Name = name;  
  38.                 this.Age = age;  
  39.                 this.Sex = sex;  
  40.             }  
  41.         }  
  42. }  
Maximum and Minimum Thread Pool Threads
 
Thread pool size is the number of threads available in a thread pool. The thread pool provides new worker threads or I/O completion threads on demand until it reaches the minimum for each category. By default, the minimum number of threads is set to the number of processors on a system. When the minimum is reached, the thread pool can create additional threads in that category or wait until some tasks complete. The thread pool creates and destroys threads to optimize throughput, which is defined as the number of tasks that complete per unit of time. Too few threads might not make optimal use of available resources, whereas too many threads could increase resource contention.
 
ThreadPool.GetAvailalbeThreads returns the number of threads that are available currently in a pool. It is the number of maximum threads minus currently active threads.
  1. int workers, ports;  
  2. // Get available threads  
  3. ThreadPool.GetAvailableThreads(out workers, out ports);  
  4. Console.WriteLine($"Availalbe worker threads: {workers} ");  
  5. Console.WriteLine($"Available completion port threads: {ports}");  
ThreadPool.GetMaxThreads and ThreadPool.GetMinThreads returns the maximum and minimum threads available in a thread pool.
  1. int workers, ports;  
  2. // Get maximum number of threads  
  3. ThreadPool.GetMaxThreads(out workers, out ports);  
  4. Console.WriteLine($"Maximum worker threads: {workers} ");  
  5. Console.WriteLine($"Maximum completion port threads: {ports}");  
ThreadPool.SetMaxThreads and ThreadPool.SetMinThreads are used to set maximum and minimum number of threads on demand as needed in a thread pool. By default, the minimum number of threads is set to the number of processors on a system.
  1. int workers, ports;  
  2. // Set minimum threads  
  3. int minWorker, minIOC;  
  4. ThreadPool.GetMinThreads(out minWorker, out minIOC);  
  5. ThreadPool.SetMinThreads(4, minIOC);  
The complete example is the following code.   
  1. using System;  
  2. using System.Threading;  
  3.   
  4. class ThreadPoolSample  
  5. {  
  6.     // Background task   
  7.     static void BackgroundTask(Object stateInfo)  
  8.     {  
  9.         Console.WriteLine("Hello! I'm a worker from ThreadPool");  
  10.         Thread.Sleep(1000);          
  11.     }  
  12.   
  13.     static void BackgroundTaskWithObject(Object stateInfo)  
  14.     {  
  15.         Person data = (Person)stateInfo;          
  16.         Console.WriteLine($"Hi {data.Name} from ThreadPool.");  
  17.         Thread.Sleep(1000);  
  18.     }  
  19.     static void Main(string[] args)  
  20.     {  
  21.         // Use ThreadPool for a worker thread        
  22.         ThreadPool.QueueUserWorkItem(BackgroundTask);  
  23.         Console.WriteLine("Main thread does some work, then sleeps.");  
  24.         Thread.Sleep(500);  
  25.      
  26.         // Create an object and pass it to ThreadPool worker thread  
  27.         Person p = new Person("Mahesh Chand", 40, "Male");  
  28.         ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  
  29.   
  30.         int workers, ports;  
  31.   
  32.         // Get maximum number of threads  
  33.         ThreadPool.GetMaxThreads(out workers, out ports);  
  34.         Console.WriteLine($"Maximum worker threads: {workers} ");  
  35.         Console.WriteLine($"Maximum completion port threads: {ports}");  
  36.       
  37.         // Get available threads  
  38.         ThreadPool.GetAvailableThreads(out workers, out ports);  
  39.         Console.WriteLine($"Availalbe worker threads: {workers} ");  
  40.         Console.WriteLine($"Available completion port threads: {ports}");  
  41.   
  42.         // Set minimum threads  
  43.         int minWorker, minIOC;  
  44.         ThreadPool.GetMinThreads(out minWorker, out minIOC);  
  45.         ThreadPool.SetMinThreads(4, minIOC);  
  46.   
  47.         // Get total number of processes availalbe on the machine  
  48.         int processCount = Environment.ProcessorCount;  
  49.         Console.WriteLine($"No. of processes available on the system: {processCount}");  
  50.   
  51.         // Get minimum number of threads  
  52.         ThreadPool.GetMinThreads(out workers, out ports);  
  53.         Console.WriteLine($"Minimum worker threads: {workers} ");  
  54.         Console.WriteLine($"Minimum completion port threads: {ports}");  
  55.   
  56.         Console.ReadKey();  
  57.     }  
  58.   
  59.         // Create a Person class  
  60.         public class Person  
  61.         {  
  62.             public string Name { getset; }  
  63.             public int Age { getset; }  
  64.             public string Sex { getset; }  
  65.   
  66.             public Person(string name, int age, string sex)  
  67.             {  
  68.                 this.Name = name;  
  69.                 this.Age = age;  
  70.                 this.Sex = sex;  
  71.             }  
  72.         }  
  73. }  
Summary
 
In this article with the code sample, we learned what a thread pool is and how to use it in .NET Core apps.