Thread pool
The full name of the thread pool is managed by the .NET Common Language Runtime (CLR) and the life cycle of the thread is handled by the CLR. Therefore, we can focus on implementing tasks without paying attention to thread management.
Application scenarios of thread pools: Task parallel library (TPL) operations, asynchronous I/O completion, timer callbacks, registered waiting operations, asynchronous method calls using delegated and socket connections.
Many people don’t know the principles of Task and Task<TResult>, because they don’t have a good understanding of thread pools.
Common properties and methods of ThreadPool
property:
property | illustrate |
---|---|
CompletedWorkItemCount | Gets the number of work items processed so far. |
PendingWorkItemCount | Gets the number of work items currently added to the processing queue. |
ThreadCount | Gets the number of thread pool threads currently present. |
method:
method | illustrate |
---|---|
BindHandle(IntPtr) | Bind the operating system handle to ThreadPool. |
BindHandle(SafeHandle) | Bind the operating system handle to ThreadPool. |
GetAvailableThreads(Int32, Int32) | Retrieves the difference between the maximum number of thread pool threads and the current active threads returned by the GetMaxThreads(Int32, Int32) method. |
GetMaxThreads(Int32, Int32) | Retrieves 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. |
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. |
QueueUserWorkItem(WaitCallback) | Queue the method for execution. This method is executed when a threaded pool thread becomes available. |
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. |
QueueUserWorkItem(Action, TState, Boolean) | Queues the method specified by the Action delegate for execution and provides 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. |
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. |
SetMinThreads(Int32, Int32) | When a new request is issued, set the minimum number of threads created on demand by thread pool before switching to the algorithm that manages thread creation and destruction. |
UnsafeQueueNativeOverlapped(NativeOverlapped) | Queuing overlapping I/O operations for execution. |
UnsafeQueueUserWorkItem(IThreadPoolWorkItem, Boolean) | Queuing the specified work item object to the thread pool. |
UnsafeQueueUserWorkItem(WaitCallback, Object) | Queuing the specified delegate to the thread pool, but not propagating the call stack to the secondary thread. |
UnsafeRegisterWaitForSingleObject(WaitHandle, WaitOrTimerCallback, Object, Int32, Boolean) | Register a delegate waiting for WaitHandle and use a 32-bit signed integer to represent the timeout in milliseconds. This method does not propagate the call stack to the helper thread. |
Thread pool description and examples
passClass, we can use thread pool.
The ThreadPool class is a static class that 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.
There won't be too much theoretical stuff here, you can refer to the official document address:/zh-cn/dotnet/api/?view=netcore-3.1
ThreadPool has oneQueueUserWorkItem()
Method, This method accepts a delegate (named WaitCallback) that represents the user's asynchronous operation. After calling this method to pass it into the delegate, it will enter the internal queue of the thread pool.
The definition of WaitCallback delegation is as follows:
public delegate void WaitCallback(object state);
Now let's write a simple thread pool example and make a nonsense.
class Program { static void Main(string[] args) { (MyAction); (state => { ("Task has been executed 2"); }); (); } // state indicates the parameter information to be passed, here is null private static void MyAction(Object state) { ("Task has been executed 1"); } }
Very simple, right?
Here are a few key points:
- Do not put long-running operations into thread pools;
- Threads in thread pools should not be blocked;
- The threads in the thread pool are all background threads (also known as worker threads);
In addition, you must remember the WaitCallback delegation here.
We observe the time it takes to create a thread:
static void Main() { Stopwatch watch = new Stopwatch(); (); for (int i = 0; i < 10; i++) new Thread(() => { }).Start(); (); ("create 10 Threads take time(millisecond):" + ); (); }
The author's computer test results are about 160.
Thread pool thread count
In the thread poolSetMinThreads()
andSetMaxThreads()
The minimum and maximum number of threads for thread pool work can be set. The definitions are as follows:
// Set the minimum number of threads for thread poolpublic static bool SetMinThreads (int workerThreads, int completionPortThreads);
// Getpublic static void GetMinThreads (out int workerThreads, out int completionPortThreads);
workerThreads: The minimum number of new worker threads to be created by the thread pool as needed.
completionPortThreads: The number of new minimum idle asynchronous I/O threads to be created by the thread pool as needed.
SetMinThreads()
The return value of represents whether the setting is successful.
// Set the maximum number of worker threads in the thread poolpublic static bool SetMaxThreads (int workerThreads, int completionPortThreads);
// Getpublic static void GetMaxThreads (out int workerThreads, out int completionPortThreads);
workerThreads: The maximum number of auxiliary threads in the thread pool.
completionPortThreads: The maximum number of asynchronous I/O threads in the thread pool.
SetMaxThreads()
The return value of represents whether the setting is successful.
I won't give an example here, but we also see the above.Asynchronous I/O threads
The following will learn relevant knowledge for this keyword.
Thread pool thread count
There is some knowledge to explain about the maximum and minimum number of threads. Previously, let's write an example:
class Program { static void Main(string[] args) { // Continuously joining the task for (int i = 0; i < 8; i++) (state => { (100); (""); }); for (int i = 0; i < 8; i++) (state => { ((1)); (""); }); ("Number of processors for this computer:" + ); // Work items and tasks represent the same meaning ("The current thread pool has thread count:" + ); ("The number of work items currently processed:" + ); ("Number of work items currently added to the processing queue:" + ); int count; int ioCount; (out count, out ioCount); ($" The default minimum number of auxiliary threads:{count},Default minimum asynchronousIONumber of threads:{ioCount}"); (out count, out ioCount); ($" 默认最大辅助Number of threads:{count},Default maximum asynchronousIONumber of threads:{ioCount}"); (); } }
After running, the author's computer outputs the results (our running results may be different):
Number of processors for this computer:8 The number of threads present in the current thread pool:8 The number of work items currently processed:2 Number of work items currently added to the processing queue:8 The default minimum number of auxiliary threads:8,Default minimum asynchronousIONumber of threads:8 默认最大辅助Number of threads:32767,Default maximum asynchronousIONumber of threads:1000
We combine the results of the operation to understand some knowledge points.
The minimum number of threads in the thread pool, the default is the current number of processors in the computer. In addition, we have seen it too. The current thread pool has 8 threads, because after the thread pool is created, there are 8 threads that survive regardless of whether there are or not.
If the thread pool minimum is set to be too large (SetMinThreads()
), which will cause the task switching overhead to increase and consume more performance resources.
If the minimum value is set less than the number of processors, it may also affect performance.
It can determine how many processors there are on the current computer (for example, the CPU is four cores and eight threads, and the result is eight).
SetMaxThreads()
The maximum number of worker threads or I/O threads set cannot be less thanSetMinThreads()
The minimum number of worker threads or I/O threads set.
Setting too many threads will cause the task switching overhead to increase and consume more performance resources.
If the added task is greater than the set maximum number of threads, it will enter the waiting queue.
The maximum number of worker threads or I/O completion threads cannot be set to be less than the number of processors on the computer.
Unsupported thread pool asynchronous delegates
After so much nonsense, we found that there is an I/O asynchronous thread number from setting the number of threads. This number of threads limits the number of threads that execute asynchronous delegation, which is exactly what this section will introduce.
Asynchronous Programming Model (APM) can be used in daily code processing.async
、await
andTask
A shuttle happened.
.NET Core is no longer usedBeginInvoke
This mode. You can follow the author to step on the pit first.
When I was reading a book, I wrote this example:
This form of examples is also used in many places, but it cannot be used in .NET Core, and can only be used in .NET Fx. . .
class Program { private delegate string MyAsyncDelete(out int thisThreadId); static void Main(string[] args) { int threadId; // Not asynchronous call MyMethodAsync(out threadId); // Create a custom delegate MyAsyncDelete myAsync = MyMethodAsync; // Initialize asynchronous delegate IAsyncResult result = (out threadId, null, null); // The current thread is waiting for asynchronous completion of the task, and can also be removed (); ("Async execution"); // Retrieve asynchronous execution results string returnValue = (out threadId, result); // closure (); ("Async processing result:" + returnValue); } private static string MyMethodAsync(out int threadId) { // Get the unique identity of the current thread in the managed thread pool threadId = ; // Simulate work requests ((new Random().Next(1, 5))); // Return to the work completion result return "Readers who like me can follow my blog ~"; } }
Many of the articles that Baidu has received are also codes in the .NET FX era. You should pay attention to C# in the version iteration, and you have made many modifications to asynchronous APIs. Don’t read other people’s articles. After learning them, you will find that you cannot use them in .NET Core (for example, I......), which is a waste of time.
The above code example also shows from the side that it was very troublesome to use asynchronously in .NET Fx (before C# 5.0).
.NET Core does not support asynchronous delegation, please see the details./dotnet/runtime/issues/16312
The official website document clearly states that it supports it/zh-cn/dotnet/api/?view=netcore-3.1#examples, and the example is the same. After working on it for so long, it doesn't work. I'll wait for the next knife to pass.
For reasons not supported, you can see here:/dotnet/migrating-delegate-begininvoke-calls-for-net-core/
If it doesn't support it, let's skip it and discuss it carefully later when learning asynchronous.
Task cancel function
This cancellation has nothing to do with thread pooling.
CancellationToken: Propagate notifications about the action that should be canceled.
CancellationTokenSource: Send a signal to the CancellationToken that should be cancelled.
The relationship between the two is as follows:
CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = ;
This cancellation lies in the occurrence of signals and the capture of signals. The cancellation of tasks is not real-time.
The sample code is as follows:
CancellationTokenSource instantiates a cancel tag and passes CancellationToken in;
The thread being started is judged at every stage..IsCancellationRequested
, and then determine whether to stop running. It depends on the thread's consciousness.
class Program { static void Main() { CancellationTokenSource cts = new CancellationTokenSource(); ("Press Enter to cancel the task"); new Thread(() => { CanceTask(); }).Start(); new Thread(() => { CanceTask(); }).Start(); (); // Cancel execution (); ("Finish"); (); } private static void CanceTask(CancellationToken token) { ("Phase One"); ((1)); if () return; ("Stage 2"); ((1)); if () return; ("Stage 3"); ((1)); if () return; ("Stage 4"); ((1)); if () return; ("Stage 5"); ((1)); if () return; } }
This cancellation mark is used in many previous synchronization methods.
Timer
There are two commonly used timers, namely: and .
is a normal timer, it is in threads in thread pool.
Packed
, and provides some other functions for dispatching on a specific thread.
What threads are safe or not? . . I don't understand this. . . But you can refer to it/questions/19577296/thread-safety-of-system-timers-timer-vs-system-threading-timer
If you want to seriously distinguish the relationship between the two, you can check out:/web/20150329101415//en-us/magazine/
The main differences between the two:
- , it will periodically trigger an event and execute code in one or more event receivers.
- , it executes a callback method on thread pool threads regularly.
Most of the time, it is used because it is relatively "light", and in addition, when .NET Core 1.0,Canceled, and it is back when NET Core 2.0. It is mainly added for the convenience of .NET FX and .NET Core migration. So, you know what I mean.
One of the constructors is defined as follows:
public Timer ( callback, object state, uint dueTime, uint period);
callback: The method to be executed regularly;
state: information (parameters) to be passed to the thread;
dueTime: Delay time, avoid starting to execute the method as soon as the timer is created;
period: Set the time interval for timed execution methods;
Timer example:
class Program { static void Main() { Timer timer = new Timer(TimeTask,null,100,1000); (); } // public delegate void TimerCallback(object? state); private static void TimeTask(object state) { (""); } }
Timer has many methods, but is not commonly used. You can check the official documentation:/zh-cn/dotnet/api/?view=netcore-3.1#methods
This is all about this article about thread pooling in C# multi-threaded series. I hope it will be helpful to everyone's learning and I hope everyone will support me more.