The Managed Thread Pool

Microsoft Silverlight will reach end of support after October 2021. Learn more.

The ThreadPool class provides your application with a pool of worker threads that are managed by the system, allowing you to concentrate on application tasks instead of thread management. If you have tasks that require background processing, the managed thread pool is an easy way to take advantage of multiple threads.

The .NET Framework uses thread pool threads for many purposes, including asynchronous I/O completion, timer callbacks, registered wait operations, System.Net socket connections, and tasks performed by the BackgroundWorker class.

For background tasks that interact with the user interface (UI), the BackgroundWorker class provides an easy way to use thread pool threads, because it communicates by raising events on the UI thread. See How to: Use a Background Worker and Synchronizing Data for Multithreading.

When Not to Use Thread Pool Threads

In the following scenarios, it is appropriate to create and manage your own threads instead of using thread pool threads:

  • You need to start a number of threads in a very short time, to execute tasks that last a second or more. As part of its thread management strategy, the thread pool delays before creating threads. Therefore, when a number of tasks are queued in a short period of time, there can be a significant delay before all the tasks are started.

  • You have many 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 have a stable identity associated with a thread, or you need to dedicate a thread to a task.

Thread Pool Characteristics

There is only one thread pool per process.

Thread pool threads are background threads. Each thread uses the default stack size, runs at the default priority, and is in the multithreaded apartment.

NoteNote:

In Silverlight, there is no difference in behavior between foreground and background threads.

Exceptions in Thread Pool Threads

Unhandled exceptions on thread pool threads terminate the process. There are three exceptions to this rule:

  • A ThreadAbortException is thrown in a thread pool thread, because Abort was called.

  • An AppDomainUnloadedException is thrown in a thread pool thread, because the application domain is being unloaded.

  • The common language runtime or a host process terminates the thread.

For more information, see Exceptions in Managed Threads.

Maximum Number of Thread Pool Threads

The number of operations that can be queued to the thread pool is limited only by available memory; however, the thread pool limits the number of threads that can be active in the process simultaneously. By default, the limit is 250 worker threads per CPU and 1,000 I/O completion threads.

Minimum Number of Idle Threads

The thread pool also maintains a minimum number of available threads, even when all threads are idle, so that queued tasks can start immediately. Idle threads in excess of this minimum are terminated to save system resources. By default, one idle thread is maintained per processor.

Using the Thread Pool

You use the thread pool by calling the ThreadPool.QueueUserWorkItem method and passing a WaitCallback delegate representing the method that performs the task. You can also queue work items that are related to a wait operation by using the ThreadPool.RegisterWaitForSingleObject method and passing a WaitHandle that, when signaled or when timed out, raises a call to the method represented by the WaitOrTimerCallback delegate. In both cases, the thread pool uses a background thread to invoke the callback method.

Thread Pool Examples

The three examples that follow demonstrate the QueueUserWorkItem and RegisterWaitForSingleObject methods.

The first example queues a very simple task, represented by the ThreadProc method, by using the QueueUserWorkItem method. No task information is supplied with this overload. Therefore, the information that is available to the ThreadProc method is limited to the object the method belongs to.

The example displays its output in a TextBlock on the UI thread. To access the TextBlock from the callback thread, the example uses the Dispatcher property to obtain a Dispatcher object for the TextBlock, and then uses the Dispatcher.BeginInvoke method to make the cross-thread call.

Imports System.Threading

Class Example

    ' This is the UI element that receives the output from the example.
    Private Shared outputBlock As System.Windows.Controls.TextBlock

    ' The Demo method runs the example. It saves the TextBlock that is 
    ' used for output, and sets up an event handler to start tasks.
    Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)

        Example.outputBlock = outputBlock
        outputBlock.Text = "Click here to start a background task." & vbCrLf

        ' Set up an event handler to start a task when the TextBlock 
        ' is clicked.
        AddHandler outputBlock.MouseLeftButtonUp, AddressOf HandleMouseUp

    End Sub

    ' Clicking the TextBlock queues a delegate to perform a task on a 
    ' thread pool thread.
    Private Shared Sub HandleMouseUp(ByVal sender As Object, _
                          ByVal e As System.Windows.Input.MouseButtonEventArgs) 

        ThreadPool.QueueUserWorkItem(AddressOf AppendTextTask)

        ' Note: The stateInfo object for the task in this example is
        ' Nothing, because this overload of QueueUserWorkItem does not
        ' take a stateInfo parameter. 
    End Sub 

    ' This method performs the task, which is to append text to the
    ' TextBlock. To communicate with objects on the UI thread, get the 
    ' Dispatcher for one of the UI objects. Use the Dispatcher object's 
    ' BeginInvoke method to queue a delegate that will run on the UI thread,
    ' and therefore can safely access UI elements like the TextBlock. In 
    ' this example, the delegate is stored in the field named 'append'.
    Private Shared Sub AppendTextTask(ByVal stateInfo As Object)

        outputBlock.Dispatcher.BeginInvoke(append, "Hello from the thread pool." & vbCrLf)

    End Sub

    ' Invoking this delegate runs the AppendText method.
    Private Shared append As New Action(Of String)(AddressOf AppendText) 

    ' The AppendText method can safely access UI elements because it is run
    ' on the UI thread, by using the Dispatcher.BeginInvoke method.
    Private Shared Sub AppendText(ByVal text As String) 

        outputBlock.Text &= text

    End Sub
End Class 

' This code produces output similar to the following:
'
'Click here to start a background task.
'Hello from the thread pool.
'Hello from the thread pool.
'Hello from the thread pool.
using System;
using System.Threading;

class Example
{
    // This is the UI element that receives the output from the example.
    private static System.Windows.Controls.TextBlock outputBlock;

    // The Demo method runs the example. It saves the TextBlock that is 
    // used for output, and sets up an event handler to start tasks.
    public static void Demo(System.Windows.Controls.TextBlock outputBlock)
    {
        Example.outputBlock = outputBlock;
        outputBlock.Text = "Click here to start a background task.\r\n";

        // Set up an event handler to start a task when the TextBlock 
        // is clicked.
        outputBlock.MouseLeftButtonUp += HandleMouseUp;
    }

    // Clicking the TextBlock queues a delegate to perform a task on a 
    // thread pool thread.
    private static void HandleMouseUp(object sender, 
                                      System.Windows.Input.MouseButtonEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(AppendTextTask);

        // Note: The stateInfo object for the task in this example is
        // Nothing, because this overload of QueueUserWorkItem does not
        // take a stateInfo parameter. 
    }

    // This method performs the task, which is to append text to the
    // TextBlock. To communicate with objects on the UI thread, get the 
    // Dispatcher for one of the UI objects. Use the Dispatcher object's 
    // BeginInvoke method to queue a delegate that will run on the UI thread,
    // and therefore can safely access UI elements like the TextBlock.
    private static void AppendTextTask(object stateInfo)
    {
        outputBlock.Dispatcher.BeginInvoke(delegate () {
            outputBlock.Text += "Hello from the thread pool.\r\n";
        });
    }
}

/* This code produces output similar to the following:

Click here to start a background task.
Hello from the thread pool.
Hello from the thread pool.
Hello from the thread pool.
 */

Supplying Task Data for QueueUserWorkItem

The following example uses the QueueUserWorkItem method to queue a task and supply the data for the task.

The example displays its output in a TextBlock on the UI thread. To access the TextBlock from the callback thread, the example uses the Dispatcher property to obtain a Dispatcher object for the TextBlock, and then uses the Dispatcher.BeginInvoke method to make the cross-thread call.

Imports System.Threading

Class Example

    ' This is the UI element that receives the output from the example.
    Private Shared outputBlock As System.Windows.Controls.TextBlock

    ' The Demo method runs the example. It saves the TextBlock that is 
    ' used for output, and sets up an event handler to start tasks.
    Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)

        Example.outputBlock = outputBlock
        outputBlock.Text = "Click here to start a background task." & vbCrLf

        ' Set up an event handler to start a task when the TextBlock 
        ' is clicked.
        AddHandler outputBlock.MouseLeftButtonUp, AddressOf HandleMouseUp

    End Sub

    ' Clicking the TextBlock queues a delegate to perform a task on a 
    ' thread pool thread. The data passed to the task is an object 
    ' representing the time the task was queued.
    Private Shared Sub HandleMouseUp(ByVal sender As Object, _
                          ByVal e As System.Windows.Input.MouseButtonEventArgs) 

        ThreadPool.QueueUserWorkItem(AddressOf AppendTextTask, DateTime.Now)

    End Sub 

    ' This method performs the task, which is to append text to the
    ' TextBlock. To communicate with objects on the UI thread, get the 
    ' Dispatcher for one of the UI objects. Use the Dispatcher object's 
    ' BeginInvoke method to queue a delegate that will run on the UI thread,
    ' and therefore can safely access UI elements like the TextBlock. In 
    ' this example, the delegate is stored in the field named 'append'.
    Private Shared Sub AppendTextTask(ByVal stateInfo As Object)

        outputBlock.Dispatcher.BeginInvoke(append, _
            "Hello from the thread pool. Task queued at: " & stateInfo & vbCrLf)

    End Sub

    ' Invoking this delegate runs the AppendText method.
    Private Shared append As New Action(Of String)(AddressOf AppendText) 

    ' The AppendText method can safely access UI elements because it is run
    ' on the UI thread, by using the Dispatcher.BeginInvoke method.
    Private Shared Sub AppendText(ByVal text As String) 

        outputBlock.Text &= text

    End Sub
End Class 

' This code produces output similar to the following:
'
'Click here to start a background task.
'Hello from the thread pool. Task queued at: 6/30/2008 4:38:54 PM
'Hello from the thread pool. Task queued at: 6/30/2008 4:39:04 PM
'Hello from the thread pool. Task queued at: 6/30/2008 4:39:05 PM
using System;
using System.Threading;

class Example
{
    // This is the UI element that receives the output from the example.
    private static System.Windows.Controls.TextBlock outputBlock;

    // The Demo method runs the example. It saves the TextBlock that is 
    // used for output, and sets up an event handler to start tasks.
    public static void Demo(System.Windows.Controls.TextBlock outputBlock)
    {
        Example.outputBlock = outputBlock;
        outputBlock.Text = "Click here to start a background task.\r\n";

        // Set up an event handler to start a task when the TextBlock 
        // is clicked.
        outputBlock.MouseLeftButtonUp += HandleMouseUp;
    }

    // Clicking the TextBlock queues a delegate to perform a task on a 
    // thread pool thread. The data passed to the task is an object 
    // representing the time the task was queued.
    private static void HandleMouseUp(object sender, 
                                      System.Windows.Input.MouseButtonEventArgs e)
    {
        ThreadPool.QueueUserWorkItem(AppendTextTask, DateTime.Now);
    }

    // This method performs the task, which is to append text to the
    // TextBlock. To communicate with objects on the UI thread, get the 
    // Dispatcher for one of the UI objects. Use the Dispatcher object's 
    // BeginInvoke method to queue a delegate that will run on the UI thread,
    // and therefore can safely access UI elements like the TextBlock.
    private static void AppendTextTask(object stateInfo)
    {
        outputBlock.Dispatcher.BeginInvoke(delegate () {
            outputBlock.Text += String.Format(
                "Hello from the thread pool. Task queued at: {0}\r\n",
                stateInfo);
        });
    }
}

/* This code produces output similar to the following:

Click here to start a background task.
Hello from the thread pool. Task queued at: 6/30/2008 4:41:54 PM
Hello from the thread pool. Task queued at: 6/30/2008 4:42:03 PM
Hello from the thread pool. Task queued at: 6/30/2008 4:42:03 PM
 */

RegisterWaitForSingleObject

The following example demonstrates several threading features:

The example displays its output in a TextBlock on the UI thread. To access the TextBlock from the callback thread, the example uses the Dispatcher property to obtain a Dispatcher object for the TextBlock, and then uses the Dispatcher.BeginInvoke method to make the cross-thread call.

Imports System
Imports System.Threading

' TaskInfo contains data that will be passed to the callback 
' method.
Public Class TaskInfo
    Public Handle As RegisteredWaitHandle = Nothing
    Public OutputBlock As System.Windows.Controls.TextBlock = Nothing
End Class 

Public Class Example

    ' The Demo method runs the example. It sets up an event handler to 
    ' signal the wait handle, saves the TextBlock that is  used for 
    ' output, and finally registers the wait handle.
    Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock) 

        outputBlock.Text = "Click here signal the wait handle." + vbCrLf

        ' Create the wait handle that the example waits on.
        Dim ev As New AutoResetEvent(False)

        ' Set up an event handler to signal the wait handle when the 
        ' TextBlock is clicked.
        AddHandler outputBlock.MouseLeftButtonUp, Function (sender As Object, _
             e As System.Windows.Input.MouseButtonEventArgs) ev.Set()

        ' Create a TaskInfo and save the TextBlock that the example uses
        ' for output.
        Dim ti As New TaskInfo()
        ti.OutputBlock = outputBlock

        ' Set the Handle property of the TaskInfo to the registered wait
        ' handle that is returned by RegisterWaitForSingleObject. This 
        ' enables the wait to be terminated when the handle has been 
        ' signaled once (see WaitProc).
        ti.Handle = ThreadPool.RegisterWaitForSingleObject( _
            ev, _
            New WaitOrTimerCallback(AddressOf WaitProc), _
            ti, _
            1000, _
            False)

    End Sub 

    ' The callback method executes when the registered wait times out,
    ' or when the WaitHandle (in this case AutoResetEvent) is signaled.
    ' WaitProc unregisters the WaitHandle the first time the event is 
    ' signaled.
    Public Shared Sub WaitProc(ByVal state As Object, ByVal timedOut As Boolean) 
        Dim ti As TaskInfo = CType(state, TaskInfo)

        Dim cause As String = "TIMED OUT"
        If Not timedOut Then
            cause = "SIGNALED"

            ' If the callback method executes because the WaitHandle is
            ' signaled, stop future execution of the callback method
            ' by unregistering the WaitHandle.
            ti.Handle.Unregister(Nothing)
        End If

        ' A UI element can be accessed safely only if the code is run on
        ' the main UI thread, by calling the BeginInvoke method for the 
        ' UI element's Dispatcher. The following code creates a generic
        ' Action delegate (with parameter types TextBlock and String) to
        ' invoke the DisplayOut helper method, and then calls the 
        ' BeginInvoke method, passing the delegate, the TextBlock, and 
        ' the reason the callback was called. 
        Dim display As New Action(Of System.Windows.Controls.TextBlock, String)( _
                AddressOf DisplayOutput)

        ti.OutputBlock.Dispatcher.BeginInvoke(display, ti.OutputBlock, cause)

    End Sub

    ' The Dispatcher.BeginInvoke method runs this helper method on the 
    ' UI thread, so it can safely access the TextBlock that is used to 
    ' display the output.
    Private Shared Sub DisplayOutput(ByVal outputBlock As _
        System.Windows.Controls.TextBlock, ByVal cause As String)

        outputBlock.Text &= _
            String.Format("WaitProc is running; cause = {0}." & vbLf, cause)
    End Sub 
End Class 

' This example produces output similar to the following:
'
'Click here to signal the wait handle.
'WaitProc is running; cause = TIMED OUT.
'WaitProc is running; cause = TIMED OUT.
'WaitProc is running; cause = TIMED OUT.
'WaitProc is running; cause = SIGNALED.
using System;
using System.Threading;

// TaskInfo contains data that will be passed to the callback 
// method.
public class TaskInfo
{
   public RegisteredWaitHandle Handle = null;
   public System.Windows.Controls.TextBlock OutputBlock = null;
}

public class Example
{
    // The Demo method runs the example. It sets up an event handler to 
    // signal the wait handle, saves the TextBlock that is  used for 
    // output, and finally registers the wait handle.
    public static void Demo(System.Windows.Controls.TextBlock outputBlock)
    {
        outputBlock.Text = "Click here signal the wait handle.\r\n";

        // Create the wait handle that the example waits on.
        AutoResetEvent ev = new AutoResetEvent(false);

        // Set up an event handler to signal the wait handle when the 
        // TextBlock is clicked.
        outputBlock.MouseLeftButtonUp += (object sender, 
            System.Windows.Input.MouseButtonEventArgs e) => ev.Set();

        // Create a TaskInfo and save the TextBlock that the example uses
        // for output.
        TaskInfo ti = new TaskInfo();
        ti.OutputBlock = outputBlock;

        // Set the Handle property of the TaskInfo to the registered wait
        // handle that is returned by RegisterWaitForSingleObject. This 
        // enables the wait to be terminated when the handle has been 
        // signaled once (see WaitProc).
        ti.Handle = ThreadPool.RegisterWaitForSingleObject(
            ev,
            new WaitOrTimerCallback(WaitProc),
            ti,
            1000,
            false
        );
    }

    // The callback method executes when the registered wait times out,
    // or when the WaitHandle (in this case AutoResetEvent) is signaled.
    // WaitProc unregisters the WaitHandle the first time the event is 
    // signaled.
    public static void WaitProc(object state, bool timedOut)
    {
        TaskInfo ti = (TaskInfo)state;

        string cause = "TIMED OUT";
        if (!timedOut)
        {
            cause = "SIGNALED";
            // If the callback method executes because the WaitHandle is
            // signaled, stop future execution of the callback method
            // by unregistering the WaitHandle.
            ti.Handle.Unregister(null);
        }

        ti.OutputBlock.Dispatcher.BeginInvoke(delegate () {
            ti.OutputBlock.Text += 
                String.Format("WaitProc is running; cause = {0}.\n", cause);
        });
    }
}

/* This example produces output similar to the following:

Click here to signal the wait handle.
WaitProc is running; cause = TIMED OUT.
WaitProc is running; cause = TIMED OUT.
WaitProc is running; cause = TIMED OUT.
WaitProc is running; cause = SIGNALED.
 */