In this section, we define a simple thread pool based on the core idea of the thread pool:
1) The number of threads used in the pool is not less than a certain number, not more than a certain number
2) create a pool when there are not enough threads, and reclaim the pool when there are rich threads.
3) task queue. When no threads are available, the task waits.
Our goal is to achieve these "requirements" without considering performance (such as waiting for a while to create new thread policies) and special processing (exceptions ), in the process of implementing this requirement, we also reviewed the basic concepts of thread and thread synchronization.
First, encapsulate the status data required by the task delegate and the task into an object:
Public classWorkitem{PublicWaitcallbackAction {Get;Set;}Public objectState {Get;Set;}PublicWorkitem (WaitcallbackAction,ObjectState ){This. Action = action;This. State = State ;}}
Create an object as a thread in the thread pool:
Public class Simplethreadpoolthread { Private object Locker = New Object (); Private Autoresetevent Are = New Autoresetevent ( False ); Private Workitem Wi; Private Thread T; Private bool B = True ; Private bool Isworking;Public bool Isworking { Get { Lock (Locker ){ Return Isworking ;}}} Public event Action < Simplethreadpoolthread > Workcomplete; Public Simplethreadpoolthread (){ Lock (Locker ){ // No actual task currently Isworking = False ;} T = New Thread (Work) {isbackground = True }; T. Start ();} Public void Setwork ( Workitem WI ){ This . Wi = wi ;} Public void Startwork (){ // Send a signal Are. Set ();} Public void Stopwork (){ // Empty task Wi = Null ;// Stop the thread Loop B = False ; // End the thread by sending a signal Are. Set ();} Private void Work (){ While (B ){ // No task, waiting for Signal Are. waitone (); If (Wi! = Null ){ Lock (Locker ){ // Start Isworking = True ;} // Execute the task WI. Action (WI. State ); Lock (Locker ){ // End Isworking = False ;} // End the event Workcomplete ( This );}}}
CodeFor more information, see comments to describe the overall structure of the Code:
1) because this thread is reused by tasks in the thread pool, the tasks in the thread are in a loop. Unless the thread pool is intended to recycle this thread, the End Task of the Loop will not be exited.
2) use automatic semaphores to wait when the thread does not have a task. The thread pool sends a signal after the task is set externally to execute the actual task. After the task is executed, it continues to wait.
3) The thread exposes a completed event. The thread pool can hook up the handling method and update the thread pool status after the task is completed.
4) All threads in the thread pool are background threads.
Next we will implement the thread pool:
Public class Simplethreadpool : Idisposable { Private object Locker = New Object (); Private bool B = True ; Private int Minthreads; Private int Maxthreads; Private int Currentactivethreadcount; Private List < Simplethreadpoolthread > Simplethreadpoolthreadlist = New List < Simplethreadpoolthread > (); Private Queue < Workitem > Workitemqueue = New Queue < Workitem > (); Public int Currentactivethreadcount { Get {Lock (Locker ){ Return Currentactivethreadcount ;}}} Public int Currentthreadcount { Get { Lock (Locker ){ Return Simplethreadpoolthreadlist. Count ;}}} Public int Currentqueuedworkcount { Get { Lock (Locker ){Return Workitemqueue. Count ;}}} Public Simplethreadpool () {minthreads = 4; maxthreads = 25; Init ();} Public Simplethreadpool ( Int Minthreads, Int Maxthreads ){ If (Minthreads> maxthreads) Throw new Argumentexception ( "Minthreads> maxthreads" , "Minthreads, maxthreads" );This . Minthreads = minthreads; This . Maxthreads = maxthreads; Init ();} Public void Queueuserworkitem ( Workitem WI ){ Lock (Locker ){ // Task Column Workitemqueue. enqueue (WI );}} Private void Init (){ Lock (Locker ){ // Create the minimum thread at the beginning For ( Int I = 0; I <minthreads; I ++) {createthread () ;}currentactivethreadcount = 0 ;} New Thread (Work) {isbackground = True }. Start ();} Private Simplethreadpoolthread Createthread (){ Simplethreadpoolthread T = New Simplethreadpoolthread (); // Hook task end event T. workcomplete + = New Action < Simplethreadpoolthread > (T_workcomplete ); // Thread Columns Simplethreadpoolthreadlist. Add (t ); Return T ;} Private void Work (){ // Main loop of the thread pool While (B ){ Thread . Sleep (100 ); Lock (Locker ){ // If there is a task in the queue and the current thread is smaller than the maximum thread If (Workitemqueue. Count> 0 & currentactivethreadcount <maxthreads ){ Workitem Wi = workitemqueue. dequeue (); // Find Idle threads Simplethreadpoolthread Availablethread = simplethreadpoolthreadlist. firstordefault (t => T. isworking = False ); // Create if none exist If (Availablethread = Null ) Availablethread = createthread (); // Set the task Availablethread. setwork (WI ); // Start the task Availablethread. startwork (); // Add an active thread Currentactivethreadcount ++ ;}}}} Private void T_workcomplete ( Simplethreadpoolthread T ){ Lock (Locker ){ // Reduce active threads Currentactivethreadcount --; // If the current number of threads is richer and more than the minimum number of threads If (Workitemqueue. Count + currentactivethreadcount) <minthreads & currentthreadcount> minthreads ){ // Stop finished threads T. stopwork (); // Delete a thread from the thread pool Simplethreadpoolthreadlist. Remove (t );}}} Public void Dispose (){ // Stop all threads Foreach ( VaR T In Simplethreadpoolthreadlist) {T. stopwork ();} // The main cycle of the thread pool stops. B =False ;}}
The thread pool structure is as follows:
1) You can set the minimum and maximum threads of the thread pool in the constructor.
2) Maintain a list of tasks and threads in a thread pool.
3) The minimum number of threads defined during thread pool Initialization
4) The main loop of the thread pool is processed every 20 milliseconds. If there is a task and the thread pool can still process the task, first find the idle thread and create
5) set the task delegate and issue the semaphore to start the task.
6) The Thread Pool provides three attributes to view the number of active threads, the total number of threads, and the number of tasks in the current queue.
7) in the callback event of task completion, we determine that if the current thread is richer and has more than the minimum thread, the thread will be recycled.
8) the thread pool is an idispose object. Stop all threads in the dispose () method and then stop the main loop of the thread pool.
Write a piece of code to test the thread pool:
Using ( Simplethreadpool T = New Simplethreadpool (2, 4 )){ Stopwatch Sw2 =Stopwatch . Startnew (); For ( Int I = 0; I <10; I ++) {T. queueuserworkitem ( New Workitem (Index => { Console . Writeline ( String . Format ( "# {0 }:{ 1}/{2 }" , Thread . Currentthread. managedthreadid, Datetime . Now. tostring ( "Mm: SS" ), Index ));Console . Writeline ( String . Format ( "Currentactivethread: {0}/currentthread: {1}/currentqueuedwork: {2 }" , T. currentactivethreadcount, T. currentthreadcount, T. currentqueuedworkcount )); Thread . Sleep (1000) ;}), I ));} While (T. currentqueuedworkcount> 0 | T. currentactivethreadcount> 0 ){ Thread . Sleep (10 );} Console . Writeline ( "All work completed" ); Console . Writeline (String . Format ( "Currentactivethread: {0}/currentthread: {1}/currentqueuedwork: {2 }" , T. currentactivethreadcount, T. currentthreadcount, T. currentqueuedworkcount )); Console . Writeline (sw2.elapsedmilliseconds );}
In the code, we push 10 tasks to the thread pool. Each task takes one second to execute. Before the task is executed, the ID of the thread to which the current task belongs, the current time and status value are output. Then, several state attributes of the thread pool are output. The main thread cyclically waits for all tasks to finish and then outputs the thread pool status attributes again and the time required to complete all tasks:
We can see that:
1) the total number of threads in the thread pool ranges from 2 to 4 to 2.
2) the number of active threads in the thread pool ranges from 2 to 4 to 0.
3) the number of tasks queued in the thread pool ranges from 9 to 0.
4) It takes three seconds for all threads to complete.
Compared. net built-in thread pool, although the performance has been improved by 0.5 seconds (see the previous article ,. net thread pool will wait about 0.5 seconds before creating a new thread), but in fact, the implementation of a good thread pool needs to consider many policies (when to create a new thread, when to recycle old threads ),. net threadpool can be well performed on the whole, so it is not recommended to use a custom thread pool at will. This example can only be used as an experiment or demonstration.