SoFunction
Updated on 2025-03-07

Introduction to the usage of ThreadPool in C# thread pool

1. ThreadPool Overview

Provides a thread pool that can be used to perform tasks, send work items, handle asynchronous I/O, wait on behalf of other threads, and handle timers.

Creating a thread takes time. If there are different small tasks to be completed, many threads can be created beforehand/requests should be made when those tasks should be completed. You don't need to create such a list yourself. This list is hosted by the ThreadPool class.

This class will increase or decrease the number of threads in the pool when needed until the maximum number of threads.

If there are more jobs to be processed, the number of threads in the thread pool has reached its limit, and the latest jobs must be queued and the thread must wait for its tasks to be completed.

Thread pool is simple to use, but it has some limitations

  • All threads in the thread pool are background threads. If all foreground threads of the process end, all background threads will stop. The thread entering the pool cannot be changed to the foreground thread.
  • The thread entering the pool cannot be set priority or name.
  • For COM objects, all threads entering the pool are multithreaded apartment (MTA) threads. Many COM objects require single-threaded apartment (MTA) threads.
  • Threads entering the pool can only be used for tasks with shorter time. If the thread is to run all the time (such as Word's spelling detector thread), you should use the Thread class to create a thread.

Operations using thread pool threads include

  • When you create a Task or Task object to execute a task asynchronously, the task schedules run on the thread pool thread by default.
  • Asynchronous timers use thread pools. Thread pool threads execute callbacks from class, and raise events from class.
  • When a registered waiting handle is used, the system thread monitors the status of the waiting handle. After waiting for the operation to complete, the worker thread from the thread pool will execute the corresponding callback function.
  • When you call the QueueUserWorkItem method to queue, the method executed on the thread pool thread is executed. To do this, the method can be passed to the WaitCallback delegate.

2. Method

  • GetAvailableThreads(Int32, Int32): Retrieve the difference between the maximum number of thread pool threads and the current number of active threads returned by the GetMaxThreads(Int32, Int32) method.
  • GetMaxThreads(Int32, Int32): Retrieve the number of thread pool requests that can be active at the same time. All requests greater than this number will remain queued until the thread pool thread becomes available.
  • SetMaxThreads(Int32, Int32): Sets the number of requests for thread pools that can be active at the same time. All requests greater than this number will remain queued until the thread pool thread becomes available.
  • GetMinThreads(Int32, Int32): When a new request is issued, the minimum number of threads created on demand by the thread pool is retrieved before switching to the algorithm that manages thread creation and destruction.
  • SetMinThreads(Int32, Int32): Set the minimum number of threads created on demand by thread pool before switching to the algorithm that manages thread creation and destruction when a new request is issued.
  • QueueUserWorkItem(WaitCallback, Object): Queue the method for execution and specify the object that contains the data used by the method. This method is executed when a threaded pool thread becomes available.
  • RegisterWaitForSingleObject(WaitHandle, WaitOrTimerCallback, Object, Int32, Boolean) : Register a delegate waiting for WaitHandle and specify a 32-bit signed integer to represent the timeout value in milliseconds.

3. Methods for setting and obtaining thread count

The maximum number of threads in the pool is configurable. In dual-core CPUs, the default settings are 1023 worker threads and 1000 I/O threads. You can also specify the minimum number of threads that should be started immediately when creating a thread pool, and the maximum number of threads available in the thread pool.

int i = 0;
int j = 0;
//The first is the auxiliary (that is, the so-called worker) thread, and the next is the I/O thread(out i, out j);
(() + "   " + ()); //The default is 1000
//Get the free thread, since no asynchronous thread is used now, it is empty(out i, out j);
(() + "   " + ()); //All are default1000

4. Queue to queue for execution: QueueUserWorkItem(WaitCallback, Object)

Queue the method for execution and specify the object that contains the data used by the method. This method is executed when a threaded pool thread becomes available.

public static bool QueueUserWorkItem ( callBack, object state);

Example:

static void Main(string[] args)
 {
     Person p = new Person(1, "Liu Bei");
     //Start the worker thread     (new WaitCallback(RunWorkerThread), p);
 }

static void RunWorkerThread(object obj)
 {
     (200);
     ("Thread pool thread starts!");
     Person p = obj as Person;
     ();
 }


 public class Person
 {
     public Person(int id, string name) { Id = id; Name = name; }
     public int Id { get; set; }
     public string Name { get; set; }
 }

5. RegisterWaitForSingleObject Register Wait Handle

Register a delegate waiting for WaitHandle and specify a number to represent the timeout value in milliseconds.

Queuing the specified method to the thread pool. When timeout or waiting for the delegate to receive a signal, the auxiliary thread will execute this method, that is, the main thread controls when the auxiliary thread starts to execute.

public static  RegisterWaitForSingleObject ( waitObject,  callBack, object state, int millisecondsTimeOutInterval, bool executeOnlyOnce);

1. Parameters

  • waitObject:WaitHandle to register. Use WaitHandle instead of Mutex.
  • callBack: The WaitOrTimerCallback delegate called when signaling to the waitObject parameter.
  • state:The object passed to the delegate.
  • millisecondsTimeOutInterval:Timeout in milliseconds.
    If 0 (zero), the function will test the state of the object and return it immediately. If -1, the timeout interval of the function will never expire. It indicates that the callback method is executed within a few seconds, which means that when the thread is just joined, it takes several seconds before the callback method will be executed for the first time. If the() method is used, use the immediate execution callback function without waiting.
  • executeOnlyOnce:If true, it means that the thread will no longer wait on the waitObject parameter after the delegate is called; if false, it means that the timer is reset until the wait is logged out after each wait operation is completed.

2. Return

  • RegisteredWaitHandle:RegisteredWaitHandle that encapsulates native handles.
// TaskInfo contains data that will be passed to the callback method.
public class TaskInfo
{
    public RegisteredWaitHandle Handle = null;
    public string OtherInfo = "default";
}


public static void Main(string[] args)
{
    // The main thread uses AutoResetEvent to signal the registered waiting handle. This waiting handle executes the callback method    AutoResetEvent ev = new AutoResetEvent(false);

    TaskInfo ti = new TaskInfo();
     = "First task";
    // The TaskInfo of this task includes the registered waiting handle returned by RegisterWaitForSingleObject.  This allows a termination of the wait when the object is signaled once (see WaitProc).     = (
        ev,
        new WaitOrTimerCallback(WaitProc),
        ti,
        1000,
        false
    );

    // The main thread waits for three seconds, and then sends a signal to demonstrate that the thread in the queue is timed out.    (3100);
    ("Main thread signals.");
    ();//Send a signal
    // The main thread sleeps, which should give the callback method execution time.  If you comment out this line, the program will usually end before the ThreadPool thread executes.    (1000);
    / /If you start a thread yourself,You can callthread . joinCome and wait for it to end。This option is not available in thread pool thread。
}

//When the registered wait timeout time is timed out, or when the signal is sent by WaitHandle (in this case AutoResetEvent), the callback method is executed.  WaitProc logs out of WaitHandle when the event is signaled for the first time.  .public static void WaitProc(object state, bool timedOut)
{
    TaskInfo ti = (TaskInfo)state;

    string cause = "TIMED OUT";
    if (!timedOut) //If Timeout is false, it means that the received signal is executed after    {
        cause = "SIGNALED";
        //If the callback method is executed because of the WaitHandle trigger signal, the anti-registration waiting handle is used to cancel the future execution of the callback method.        if ( != null)
            (null);//
    }

    ("WaitProc( {0} ) executes on thread {1}; cause = {2}.",
        , ().ToString(), cause);//Execute after timeout}

The results are as follows:

WaitProc( First task ) executes on thread 7; cause = TIMED OUT.
WaitProc( First task ) executes on thread 7; cause = TIMED OUT.
WaitProc( First task ) executes on thread 7; cause = TIMED OUT.
Main thread signals.
WaitProc( First task ) executes on thread 7; cause = SIGNALED.

This is all about this article about C# thread pool ThreadPool. I hope it will be helpful to everyone's learning and I hope everyone will support me more.