I. Applicability of Thread Pool
Generally, a Thread example is constructed during routine multi-threaded development, and then Start is called for execution. If a thread spends most of its time waiting for an event to respond, or if a thread is repeatedly created for a certain period of time. In these cases, I personally feel that using the ThreadPool is better than simply creating a Thread. This is because the thread pool can extract Idle threads and use them as needed. When the threads are used, the thread is recycled to achieve the effect of reusing objects. This involves the nature of the pool. Threads are easily associated with database connections, streams, and Socket sockets. However, I personally think Thread is not a non-hosted resource and there is a low-level way to identify it, that is, the Thread does not implement the IDispose interface. If you use Reflector to open it and view it, there will be an destructor ~ Thread () actually calls an external method InternalFinalize (). It is estimated that this involves something in CLR. If threads are enabled frequently, the consumption of resources will be more than that of the thread pool.
2. Pool capacity and Object Management
Since the nature of the pool is mentioned above, we can also see the characteristics of an object pool in TheardPool. This can be used as a reference when we create an object pool in the future. Although I have previously written a Socket Object pool, it does not run well. I do not know whether a Socket Object pool exists in the CLR. But I read Lao Zhao's blog and found that the CLR actually has an object pool for database connection, the implementation effect is similar to that of ThreadPool, allowing the object to be reused.
In the previous definition of the Socket pool, only one object ceiling was defined without a lower limit. In ThreadPool, the upper and lower limits of objects in the pool can be set and obtained.
SetMinThreads( workerThreads, SetMaxThreads( workerThreads, GetMaxThreads( workerThreads, GetMinThreads( workerThreads, completionPortThreads);
There are two reasons for this thread to be mentioned later. MinThread refers to the minimum number of threads that are retained when the thread pool is initially or idle. This value is related to the CLR version and the number of CPU cores. In versions earlier than CLR SP1, the default maximum number of threads in the thread pool is, and the default maximum number of threads after CLR SP1 is. The minimum number of threads is, so I tried it. However, the relationship between CLR and. NET Framework is involved here.
. NET Framework | CLR
---------------------------------------
2.0 RTM | 2.0.50727.42
2.0 SP1 | 2.0.50727.1433
2.0 SP2 | 2.0.50727.3053
3.0 RTM | 2.0 RTM
3.0 SP1. | 2.0 SP1
3.0 SP2 | 2.0 SP2
3.5 RTM | 2.0 SP1
3.5 SP1 | 2.0 SP2
4.0 RTM | 4.0.30319.1
I use the Version attribute of the Environment class to obtain the CLR Version number. The following code uses several versions of. NET Framework for compilation.
i1, + i1++ i1,+i1 + +, Environment.Version);
The result is a bit disappointing. The disappointment is not contrary to the above. But the. NET Framework I use here is incomplete.
The CLR versions 2.0 and 3.5 are in the SP2 version.
The result of 3.5 is as follows:
The result of 2.0 is as follows:
The result shows that the maximum number of threads is consistent with the minimum number of threads. I am using an i5 processor, dual-core, and four threads.
Below I ran this on a virtual machine, a single-core Virtual Machine
NET Framework1.0 is used, and CLR is 1.0. It is true that the minimum number of threads and the maximum number of working threads are equivalent, but the number of I/O threads is still 1000. Finally, let's take a look at the familiar. NET 4.0
I ran on the Virtual Machine and the local machine separately. The I/O thread is still the same as 1000 and remains unchanged. It is estimated that the previous formula is not applicable to it, but the number of jobs is still a little strange. There are only 1023 Single-core jobs, but on i5, it is not a multiple of 1024.
When the thread pool object is used, it does not feel like calling another object, but similar to the request and response methods of the Web server. This concept is a little different from the Socket pool I designed. When the thread pool is not submitted with any task request to the thread pool, the number of threads actually created in the thread pool is not that large, in fact, it is only less than or equal to the minimum number of threads. Refer to Lao Zhao's code
maxCount = minCount = Stopwatch watch = WaitCallback callback = i => Console.WriteLine(String.Format( Thread.Sleep( Console.WriteLine(String.Format( ( i = ; i < ; i++ }
The running result is as follows:
It can be seen that when a request is initiated, the thread pool can immediately respond to the request to process the task, and 16 pieces of information can be completed within one second, the 16 is equal to the minimum number of threads. Lao Zhao's blog says that the number of threads created in one second is smaller than the minimum number of threads. It is estimated that I can still use the processor performance. However, I am also running on a single-core virtual machine, and the number of threads created in one second is equal to the minimum number of threads. At the same time, I also found another situation: running the above Code on a real computer, setting the minimum number of threads to a value smaller than 4, and creating four threads at the same time at the beginning, I personally think this has a lot to do with the i5CPU with a dual-core and four threads. This is not the case if it runs on a virtual machine.
Since the number of threads initially created is not the maximum number of threads, it is not necessary to create a new thread until the maximum value is reached when the thread pool is used, this design greatly reduces resource usage. It also raises another problem: the thread creation speed, which affects the request response time. Each request must be responded as soon as possible, but if the response speed is too fast, in case a large number of short tasks flood into the thread pool at an instant, it is also a large overhead to recycle used threads after a task is completed. Therefore, the thread creation speed must be well-tuned. After reading and running the code of Lao Zhao, I found that two threads are created in one second, but most of them only create one thread in one second. I made some changes myself to make the results clearer.
Dictionary<, TimeSpan> createTime = Dictionary<, TimeSpan> maxCount = minCount = Stopwatch watch = WaitCallback callback = i => ( TimeSpan ts = (! createTime[Thread.CurrentThread.ManagedThreadId] = Console.WriteLine( Thread.Sleep( ( i = ; i < ; i++
Similarly, the code running Lao Zhao may not be able to see the creation of two threads per second. It seems that my code segment is even harder to see, probably because of the lock.
I tried many times to get this result. It seems that the example is very blunt, but one thread can be clearly seen in one second.
Iii. Classification of objects in a pool
Mentioned in the section about obtaining and setting the upper and lower limits of a thread pool. There are two types of threads in a thread pool, one is a working thread and the other is an IO thread. The two threads are used differently. When a task request is sent to the thread pool, that is, when the QueueUserWorkItem or UnsafeQueueUserWorkItem method is called. The thread used is the worker thread. When using APM mode, some of them use the Working thread and some others use the I/O thread. Here, most of them use working threads, and only a few use I/O threads. I/O threads are used only when real Asynchronous Method callback is used. The BeginXXX/EndXX methods of which classes actually use Asynchronization are mentioned in my post. However, I have read Lao Zhao's blog and obtained a result. Even asynchronous FileStream, Dns, Socket, WebRequest, and SqlCommanddeng operations will call the threads in the thread pool. Different threads are called at different stages. First, let's take a look at the following code. Note that if you want to set the upper and lower limits of the thread pool to the same value, you can set the lower limit first and then the upper limit, otherwise, the upper limit is restored to the default value.
ThreadPool.SetMinThreads(, ThreadPool.SetMaxThreads(, ManualResetEvent waitHandle = ManualResetEvent( ( i = ; i < ; i++ FileStream fs = FileStream( + i + , FileMode.Create, FileAccess.Write, FileShare.Write, content = [] arr = fs.BeginWrite(arr, , arr.Length, (asyncPara) => FileStream caller = asyncPara.AsyncState ThreadPool.GetAvailableThreads( workC, Console.WriteLine(String.Format( }
The GetAvailableThreads method of the ThreadPool of the thread pool is used here. The method description is to get the difference between the maximum number of threads in the thread pool and the number of threads currently in use. In my opinion, it is to get the number of Idle threads in the thread pool. As mentioned in the previous article, the thread callback method will be opened during Asynchronous Method callback, And the thread to be opened is from the thread pool, this Code only calls the Asynchronous Method of the FileStream class, and does not call other methods of the thread pool. View running results
Obviously, in asynchronous file write operations, the working thread is used, and the IO thread is also used. This result is different from my previous guess. Originally, when the Begin method was called for asynchronous operations, the system API was used to allow devices to directly access the memory. After the DMA was completed, an IO thread was opened for method callback. However, after reading this situation, I think that after the Begin method is called, the thread pool uses an IO thread to call the system API to allow devices to access the memory, the thread used after the end is a working thread. If the working thread is used up at this time, the thread that calls "True asynchronous" or "false asynchronous" will be blocked. Of course, the callback function is used here, so the result of the callback function is not needed. Set the worker thread to only one.
ThreadPool.SetMinThreads(, ThreadPool.SetMaxThreads(, ManualResetEvent waitHandle = ManualResetEvent( ( i = ; i < ; i++ ThreadPool.QueueUserWorkItem((para) => content = content += [] arr = ( i = ; i < ; i++ FileStream fs = FileStream( + i + , FileMode.Create, FileAccess.Write, FileShare.Write, IAsyncResult result = fs.BeginWrite(arr, , arr.Length, , }
The file can still be output normally without being affected by the running thread. However, I/O threads and working threads are not unrelated. In Lao Zhao's blog, if the working thread is used up, and the BeginGetResponse Asynchronous Method of WebRequest is called, an InvalidOperationException exception is thrown, threadPool does not have enough free threads to complete this operation. But not all asynchronous operations will have this problem, just like the above FileStream.
4. scenarios where thread pools are not applicable
Based on the results obtained in the above experiment, I will introduce the applicable scenarios of ThreadPool at the beginning of this article. Now I also talk about the scenarios where the thread pool is not applicable. If you want to adjust the thread priority, open the thread by yourself! All threads in the thread pool have the Normal priority by default. If the task is executed for a long time, we recommend that you open the thread by yourself, because it may block the threads in the thread pool and eventually cause the threads in the thread pool to be exhausted. If the task is to be executed immediately, we recommend that you use the thread pool because all tasks submitted to the thread pool need to be queued. The speed of creating a new thread in the thread pool is no more than one second.
Finally, I have attached the connection to the three blogs of Lao Zhao. You may feel that you are welcome to criticize and correct the mistakes you have made. If you have any suggestions or comments, let us talk about them. Thank you!
Thread Pool (top): The role of thread pool and CLR Thread Pool
Thread Pool (medium): role of independent thread pool and IO Thread Pool
Thread Pool (below): related test and precautions