Using the ThreadPool in C#

By FoxLearn 1/16/2025 7:36:38 AM   47
Multi-threading is a powerful concept that can significantly enhance the performance of applications by allowing multiple tasks to run concurrently.

However, it often comes with its own set of challenges chief among them, managing threads efficiently. In C# .NET, the ThreadPool provides an elegant solution to these challenges, making it easier to work with threads.

What is a ThreadPool?

A ThreadPool is a collection of threads that are available for executing tasks. When you use a ThreadPool, you don’t need to manually manage individual threads. Instead, you queue a work item, and one of the available threads from the pool picks it up and executes it. This makes it much easier to implement multi-threading, as the system automatically handles the details of thread management.

While you still need to ensure that threads don’t interfere with each other, and you might care about when the tasks are completed, the ThreadPool abstracts away much of the complexity.

Multi-Threading in C# with the ThreadPool

For example, how to use the ThreadPool in C#.

Below is a simple application that submits multiple work items to the ThreadPool, waits for them to finish, and then prints the results:

using System;
using System.Threading;

namespace ThreadPoolTest
{
    class Program
    {
        // Define the number of threads (work items) to be processed
        private const int NumThreads = 5;

        // Arrays to hold input values, result values, and ManualResetEvent objects
        private static int[] inputArray;
        private static double[] resultArray;
        private static ManualResetEvent[] resetEvents;

        // Main method - entry point of the program
        private static void Main(string[] args)
        {
            // Initialize the arrays to store input data, result data, and synchronization events
            inputArray = new int[NumThreads];
            resultArray = new double[NumThreads];
            resetEvents = new ManualResetEvent[NumThreads];

            // Create a Random object to generate random input values
            Random rand = new Random();

            // Loop to initialize the input array, create corresponding ManualResetEvent, and queue work items
            for (int s = 0; s < NumThreads; s++)
            {
                // Assign a random number between 1 and 5,000,000 to each input element
                inputArray[s] = rand.Next(1, 5000000);

                // Initialize a ManualResetEvent for each thread, initially in the non-signaled state (false)
                resetEvents[s] = new ManualResetEvent(false);

                // Queue a work item (DoWork method) for the ThreadPool, passing the index as an argument
                ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), (object)s);
            }

            // Print message indicating that the program is waiting for work items to complete
            Console.WriteLine("Waiting...");

            // Wait until all work items are completed (all ManualResetEvents are signaled)
            WaitHandle.WaitAll(resetEvents);

            // Once all work is completed, print the results for each work item
            Console.WriteLine("And the answers are: ");
            for (int i = 0; i < NumThreads; i++)
                // Print each input value along with the corresponding result
                Console.WriteLine(inputArray[i] + " -> " + resultArray[i]);
        }

        // Method to be executed by each thread in the ThreadPool
        private static void DoWork(object o)
        {
            // Cast the passed argument (index) back to an integer
            int index = (int)o;

            // Perform a summation calculation (series) based on the input value
            for (int i = 1; i < inputArray[index]; i++)
                resultArray[index] += 1.0 / (i * (i + 1));

            // After completing the task, signal the corresponding ManualResetEvent to notify completion
            resetEvents[index].Set();
        }
    }
}

Using the ThreadPool in C# simplifies the process of working with multiple threads.