Summary: in this tutorial, you’ll learn how to use the C# Threadpool
class to manage threads more efficiently.
Introduction to the C# Threadpool
In the previous tutorial, you learned how to develop multithreaded applications by manually creating threads using the Thread
class.
Typically, the number of threads that the application need is unknown beforehand. Also, the optimal number of threads that an application should have depends on some external factors including:
- The number of CPU cores.
- The type of tasks that need to be executed.
- System performance.
If you don’t consider these factors carefully, you may overload the system.
A solution to this problem is to use a thread pool.
By definition, a thread pool is managed pool of threads that can be used to execute tasks concurrently. The threads managed by the thread pool are often called worker threads. The worker threads are optimized for short-running tasks. Also, the worker threads are background threads.
The thread pool works as follows:
- First, a thread pool spawns a number of threads upfront.
- Second, when you submit a task to a thread pool, the thread pool adds the task to a queue and assigns a thread from a pool to execute the task.
- Third, once the thread completes the task, the thread pool returns the thread to the pool, ready for executing the next task.
By doing this, the thread pool can reuse threads and avoid the overhead of creating and destroying threads for each task, which is very expensive in terms of time and resources.
The following picture illustrates how the thread pool works:
To create a thread pool, you use the
class. The ThreadPool
class provides you with the ThreadPool
QueueUserWorkItem
for submitting tasks to the thread pool.
The QueueUserWorkItem
method queues a method for execution. In other words, the method executes when a thread in the thread pool is ready for execution.
The QueueUserWorkItem
method accepts an instance of the WaitCallback
delegate with the following signature.
public delegate void WaitCallback(object? state);
Code language: C# (cs)
In the WaitCallback
delegate, the state
argument is an object that contains information used by the callback.
C# Threadpool example
The following example demonstrates how to use the ThreadPool
class to create a thread pool:
using static System.Console;
static void CheckHttpStatus(string url)
{
HttpClient client = new();
var response = client.GetAsync(url).Result;
WriteLine($"The HTTP status code of {url} is {response.StatusCode}");
}
List<string> urls = new(){
"https://www.google.com/",
"https://www.duckduckgo.com/",
"https://www.yahoo.com/",
};
foreach (var url in urls)
{
ThreadPool.QueueUserWorkItem((state) => CheckHttpStatus(url));
}
// wait for all thread to complete
// and press a key
Console.Read();
Code language: C# (cs)
How it works.
First, define a static method CheckHttpStatus
to check the HTTP status code of an URL:
static void CheckHttpStatus(string url)
{
HttpClient client = new();
var response = client.GetAsync(url).Result;
WriteLine($"The HTTP status code of {url} is {response.StatusCode}");
}
Code language: C# (cs)
Second, create a list of URLs
to check:
List<string> urls = new(){
"https://www.google.com/",
"https://www.duckduckgo.com/",
"https://www.yahoo.com/",
};
Code language: C# (cs)
Third, check the HTTP status code of each URL of the list by passing a lambda expression to the QueueUserWorkItem()
method of the ThreadPool
method:
foreach (var url in urls)
{
ThreadPool.QueueUserWorkItem((state) => CheckHttpStatus(url));
}
Code language: C# (cs)
Finally, add the
that blocks the main thread to wait for all threads in the thread pool to be completed.Console.Read
()
It’s important to note that you can wait for all the tasks to be completed but we’ll cover it in the subsequent tutorial.
Summary
- Use a thread pool to improve the application’s performance by making efficient use of available resources and reducing the overhead of creating and destroying threads.
- Use the
ThreadPool
class to create a thread pool. - Use the
QueueUserWorkItem
method of theThreadPool
class to submit tasks to the thread pool.