3/24/15

Using the Thread Pool in .NET

The Microsoft.Net framework provides a Thread Pool, which is a pool of threads available for applications to use. There is one Thread Pool per CLR and all the Application Domains managed by the CLR will have access to the threads in that Thread Pool.

The applications do not need to go through the hassle of creating and managing the threads as all that is taken care of by the Thread Pool. Using the threads in the Thread Pool is more efficient in terms of resources since the threads can be reused. Using the Thread Pool threads will also lead to an improvement in performance since the creation of threads is a time consuming operation that could negatively impact performance.

One way for applications to use the threads in the Thread Pool is to call the ThreadPool.QueueUserWorkItem method in the System.Threading namespace. This method takes a method and related parameters as input and adds an entry into the Thread Pool's global queue.


//we are adding the method ProcessCreditCard to the ThreadPool queue
System.Threading.ThreadPool.QueueUserWorkItem(ProcessCreditCard, 100);
//this method will be executed by a thread pool thread private void ProcessCreditCard(object input)
{
   //do something here
}
When a thread becomes available in the Thread Pool, it will take this entry from the queue and execute it. The drawback of using this method is that there is no way to interact with the Thread Pool thread that is executing your task. We do not know if the method completed successfully or if there were any exceptions. We also do not have the ability to get a return value from the method. To fix these limitaions Microsoft introduced the concept of Tasks.

Task is a class in the System.Threading.Tasks namespace. It provides an alternative for scheduling tasks for execution by the Thread Pool.

//create a task that will schedule a delegate(ProcessAmount) that returns a bool
 Task t1 = new Task(ProcessAmount);
//this line queues up the delegate for execution by a Thread Pool thread   t1.Start();
//we are waiting for the execution to complete   t1.Wait();
//we are printing the result from the execution   Console.WriteLine(t1.Result);
//this is the delegate that will be executed by a thread pool thread  private bool ProcessAmount()
 {
        return true;
 }
As you can see, we can schedule a task that returns a bool, wait for it to complete and then get the result back.

We also have the ability to cancel the task if necessary.

 CancellationTokenSource source = new CancellationTokenSource();
//this line queues up the delegate for execution by a Thread Pool thread Task t1 = new Task(()=>ProcessAmount(source.Token));
 t1.Start(); source.Cancel();
 private static bool ProcessAmount(CancellationToken token)   {
    //some periodic operation
   //check if the task has been cancelled,    //if yes, this method will throw an OperationCanceledException       token.ThrowIfCancellationRequested();       return true;
   }
The calling code can capture the exception thrown by the Task on cancellation and do whatever is necessary.

The System.Threading.Tasks namespace also contains a static class called Parallel that provides two methods For and ForEach.

Both of them internally use Task objects to execute the code using the Thread Pool threads

//regular For
for(int i=0;i<100 i="" p="">//do something
//Parallel's For
Parallel.For(0,100, SomeDelegate);
When we call the For method of the Parallel class, the thread pool thread's perform this task in parallel resulting in improved performance.It is ideal for situations where you want to execute a large number of tasks in parallel OR if there are long-running tasks. Few operations or short operations are not good for executing in parallel. This is also not an ideal option if the processing needs to happen sequentially since the Thread Pool threads execute in no particular order. This option is also not ideal if the operations share some data since this would require some kind of synchronization that will negatively impact the performance.

Parallel.ForEach and Parallel.Invoke also work similarly.

Parallel LINQ

When dealing with collections via LINQ, we can use Tasks(a.k.a the Thread Pool threads) to execute the operations on the collections in parallel in order to improve performance. This is possible via the AsParallel method of the System.Linq.ParallelEnumerable class. This method can be used to convert sequential queries based on IEnumerable or IEnumerable to parallel queries as follows:

var filteredList = GetItems.AsParallel().where(//do some filtering here);
private IEnumerable GetItems()
{

   //return a list
}
As is the case with Parallel.For and Paralle.ForEach and Parallel.Invoke, Parallel LINQ is ideal for situations where you want to execute a large number of tasks in parallel OR if there are long-running tasks. Few operations or short operations are not good for executing in parallel. This is also not an ideal option if the processing needs to happen sequentially since the Thread Pool threads execute in no particular order. This option is also not ideal if the operations share some data since this would require some kind of synchronization that will negatively impact the performance.

(Reference: CLR via C# by Jeffrey Richter)