Home > Articles > Programming > C#

.NET Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

The Managed Thread Pool

Last updated Mar 14, 2003.

If you’re looking for more up-to-date information on this topic, please visit our .NET and Windows Programming article, podcast, and store pages.

The .NET Framework maintains a pool of reusable worker threads that are ideal for short, focused tasks. As I pointed out above, the Framework uses these threads for asynchronous file I/O and for asynchronous operations that programs perform by calling BeginInvoke on a delegate.

Because these operations are typically short, the overhead of creating and destroying a thread can take a significant portion of the execution time. To prevent thread management overhead from impacting performance, the Framework creates pool threads as needed, up to a pre-defined limit, and then keeps the threads in a suspended state so that they are ready for the next asynchronous operation. It does take a small amount of memory for each thread, but the memory overhead is well worth the performance gain of not having to create and destroy threads.

By default, the system allows a maximum of 25 thread pool threads per processor core. On a dual-processor (or dual-core) system, the default maximum number of thread pool threads is 50. The System.Threading.ThreadPool class provides a number of static methods that allow you to monitor and control the thread pool.

GetMaxThreads retrieves the maximum number of thread pool worker threads and asynchronous I/O threads allowed in the thread pool. You pass two integer parameters to the function by reference, and the values for worker threads and I/O threads are returned in those parameters.

GetAvailableThreads retrieves the difference between the maximum number of thread pool threads and the number of currently active threads. Like GetMaxThreads, this method takes two integer reference parameters in which the values are returned.

GetMinThreads retrieves the number of idle threads that the thread pool maintains in anticipation of new requests. These threads are fully constructed and kept idle so that a new request can be serviced immediately, without having to construct a new thread. As requests are filled and threads become idle, a background task terminates idle threads in excess of the minimum value in order to save system resources.

If you find that you often have more concurrent background threads than the minimum, you can increase performance by calling SetMinThreads to raise the minimum, thereby forcing the Framework to maintain at least the number of threads that you expect to have. Be careful, though. If you reduce the number of idle threads to less than the number of processors, you can seriously impact performance. Making too many idle threads also can cause performance problems because each thread requires system resources. As the documentation states: "finding the right balance is a performance tuning issue."

In .NET 2.0 and later, you can call SetMaxThreads to set the maximum number of worker threads and I/O completion threads allowed in the thread pool. There are some limitations. You cannot set the number of threads lower than the number of processors, and if the common language runtime is hosted (by IIS or SQL Server, for example), the host can limit the number of thread pool threads. Finally, setting the thread pool size too large can cause performance problems because if there are too many concurrent threads, task switching overhead becomes a serious bottleneck. In .NET 1.0 and 1.1, only the host could set the number of threads—by calling the CorSetMaxThreads Windows API function.

The program below illustrates how to use the ThreadPool static methods to control and monitor the managed thread pool.

[C#]

using System;
using System.Threading;

namespace pool_cs
{
  class Program
  {
    static void Main(string[] args)
    {
      // Show default thread stats
      ShowThreadStats();

      // Change thread pool parameters
      Console.WriteLine("Changing number of threads...");
      if (!ThreadPool.SetMaxThreads(100, 500))
      {
        Console.WriteLine("Call to SetMaxThreads failed.");
      }
      if (!ThreadPool.SetMinThreads(25, 25))
      {
        Console.WriteLine("Call to SetMinThreads failed.");
      }

      // Start a thread pool thread
      ThreadPool.QueueUserWorkItem(new WaitCallback(DoNothing));
      Thread.Sleep(10);

      // Show new thread stats
      ShowThreadStats();

      // And wait for user to terminate program
      Console.WriteLine();
      Console.WriteLine("Press Enter...");
      Console.ReadLine();
    }

    static void ShowThreadStats()
    {
      // Retrieve maximum number of thread pool threads
      int workerThreads;
      int completionThreads;
      ThreadPool.GetMaxThreads(out workerThreads, out completionThreads);
      Console.WriteLine("Max worker threads={0}\nMax I/O completion threads={1}",
        workerThreads, completionThreads);

      // Retrieve minimum idle threads
      ThreadPool.GetMinThreads(out workerThreads, out completionThreads);
      Console.WriteLine("Min worker threads={0}\nMin I/O completion threads={1}",
        workerThreads, completionThreads);

      // Show available threads
      ThreadPool.GetAvailableThreads(out workerThreads, out completionThreads);
      Console.WriteLine("Available worker threads={0}\nAvailable I/O completion threads={1}",
        workerThreads, completionThreads);
    }

    static void DoNothing(object state)
    {
      Thread.Sleep(1000);
      Console.WriteLine("Doing nothing...");
    }
  }
}

[Visual Basic]

Imports System.Threading
Module Module1

  Sub Main()
    ’ Show default thread stats
    ShowThreadStats() ’

    ’ Change thread pool parameters
    Console.WriteLine("Changing number of threads...")
    If Not ThreadPool.SetMaxThreads(100, 500) Then
      Console.WriteLine("Call to SetMaxThreads failed.")
    End If
    If Not ThreadPool.SetMinThreads(25, 25) Then
      Console.WriteLine("Call to SetMinThreads failed.")
    End If

    ’ Start a thread pool thread
    ThreadPool.QueueUserWorkItem(AddressOf DoNothing)
    Thread.Sleep(10)

    ’ Show new thread stats
    ShowThreadStats()

    ’ And wait for user to terminate program
    Console.WriteLine()
    Console.WriteLine("Press Enter...")
    Console.ReadLine()

  End Sub

  Sub ShowThreadStats()
    ’ Retrieve maximum number of thread pool threads
    Dim workerThreads As Integer
    Dim completionThreads As Integer
    ThreadPool.GetMaxThreads(workerThreads, completionThreads)
    Console.WriteLine("Max worker threads={0}" & vbCrLf & "Max I/O completion threads={1}", _
        workerThreads, completionThreads)

    ’ Retrieve minimum idle threads
    ThreadPool.GetMinThreads(workerThreads, completionThreads)
    Console.WriteLine("Min worker threads={0}" & vbCrLf & "Min I/O completion threads={1}", _
        workerThreads, completionThreads)

    ’ Show available threads
    ThreadPool.GetAvailableThreads(workerThreads, completionThreads)
    Console.WriteLine("Available worker threads={0}" & vbCrLf & "Available I/O completion threads={1}", _
        workerThreads, completionThreads)

  End Sub

  Sub DoNothing(ByVal state As Object)
    Thread.Sleep(1000)
    Console.WriteLine("Doing nothing")
  End Sub
End Module

This program displays the thread pool parameters at startup and then changes the maximum and minimum numbers of threads by calling SetMaxThreads and SetMinThreads, respectively. It then starts a thread by calling ThreadPool.QueueUserWorkItem, and displays the thread pool parameters again to show that the changes took effect and that the newly started thread reduces the number of available threads.