Simple and practical thread pool components that can be applied online, simple and practical online
0 Preface
There are many components in the thread pool on the Internet. I tried to write a demo myself before, but these components are generally relatively simple and do not fully implement the functions of the background thread pool component. Therefore, here we implement a thread pool component that can be used in the online environment. This thread pool component has the characteristics of the thread pool application, as shown below:
1. Scalability: the number of threads in the thread pool should change dynamically. You can apply for more threads When busy, and cancel some threads when idle.
2. thread status: introduces sleep and wake-up mechanisms for thread management in the thread pool. When no task is running, the thread is sleep.
3. thread management: applications for and cancellation of threads in the thread pool are managed not by creating a separate thread, but by the thread pool automatically.
Finally, the thread pool we implement has the following features:
1. The number of threads in the thread pool is between min and max;
2. threads in the thread pool have sleep and wake-up mechanisms;
3. When the thread sleep for more than one second in the thread pool, end a thread. When the thread pool lasts for one second without Idle threads, a new thread is created.
1. Key Data Structure
The main data structure includes two CThreadPool and CThreadWorker.
Here, we first define the data structure and briefly describe their functions.
The data structure is defined as follows:
1 class CThreadWorker 2 {3 CThreadWorker * next _; // the thread in the thread pool is a single-chain table structure to save 4 5 int wake_up _; // wake up sign 6 pthread_cond_t wake _; 7 pthread_mutex_t mutex _; 8 9 pthread_t tid _; 10 void (* func) (void * arg); // function execution body 11 void * arg; // function execution body parameter 12 time_t sleep_when _; // thread sleep time 13}; 14 15 class CThreadPool16 {17 CThreadWorker * head _; 18 19 unsigned int min _; // minimum number of threads 20 unsigned int cur _; // current number of threads 21 unsigned int max _; // maximum number of threads 22 23 time_t last_empty _; // no thread time in the last thread pool 24 pthread_mutex_t mutex _; 25 };
The CThreadPool is used to manage the thread pool and record the number of threads in the thread pool, the minimum number of threads, and the maximum number of threads.
CThreadWorker is the entity that stores thread records. It stores the execution function body, function parameters, and sleep time of the thread. Another semaphore is used to sleep or wake up a thread.
When we create a thread pool, that is, the min CThreadWorker is created by default. By default, every thread entity is sleep and blocked at pthread_cond_wait. When we execute the user function, obtain the thread from the thread pool, change the func, arg, and other parameters of the thread, and then wake up the thread.
In the middle, the thread pool manages threads to ensure the scalability of the thread pool.
2 source code 2.1 header file
1 # ifndef _ THREAD_POOL_H _ 2 # define _ THREAD_POOL_H _ 3 4 # include <pthread. h> 5 # include <time. h> 6 7 struct CThreadWorker 8 {9 CThreadWorker * next _; // the thread in the thread pool is a single-chain table structure that stores 10 11 int wake_up _; // The wake-up sign 12 pthread_cond_t wake _; 13 pthread_mutex_t mutex _; 14 15 pthread_t tid _; 16 void (* func) (void * arg); // function execution body 17 void * arg; // function execution body parameter 18 time_t sleep_when _; // thread sleep time 19}; 20 21 struct CThreadPool22 {23 CThreadWorker * head _; 24 25 unsigned int min _; // minimum thread count 26 unsigned int cur _; // current thread count 27 unsigned int max _; // maximum thread count 28 29 time_t last_empty _; // no thread time in the last thread pool 30 pthread_mutex_t mutex _; 31}; 32 33 CThreadPool * CreateThreadPool (unsigned int min, unsigned int max ); 34 int StartWork (CThreadPool * pool, void (* func) (void * arg), void * arg); 35 # endif
2.2 Implementation File
The implementation file mainly completes CreateThreadPool and StartWork in. h. Let's give a rough description of the flowchart.
2.2.1 CreateThreadPool process description
Shows the process for creating a thread pool. The most critical step is DoProcess. When DoProcess is executed, min threads are created by default, and all threads are sleeping due to signals. StartWork will get a thread, modify the func and arg parameters of the thread, and then wake up the thread to execute our task. When a thread is executed, we need to determine the current state of the thread pool, whether to create a new thread, add the thread to the thread pool again, or cancel the thread. The specific logical diagrams are not easy to describe. We will give comments in the following code.
2.2.2 StartWork process description
For the process of starting a task, we have already described 2.2.1 that when starting a task, we will take out a thread from the thread pool, modify the func/arg attribute of the thread, and then wake up the thread. After the thread is executed, it must be re-added to the thread pool or logged out, which is processed in DoProcess 2.2.1.
2.3 Implementation File
1 # include <stdlib. h> 2 # include "pthread_pool.h" 3 4 // The thread pool lasts for 1 second without Idle threads 5 # define WaitWorkerTimeout (pool) (time (NULL) -pool-> last_empty _)> 1) 6 // There are no threads in the thread pool, and all threads have pop out to execute specific tasks. 7 # define NoThreadInPool (pool) (pool-> head _ = NULL) 8 # define CanCreateThread (pool) (pool-> cur _ <pool-> max _) 9 10 11 static int CreateOneThread (CThreadPool * pool); 12 static void * DoProcess (void * arg); 13 static void Pu ShWork (CThreadPool * pool, CThreadWorker * worker); 14 static void PopWork (CThreadPool * pool, CThreadWorker * worker); 15 static void InitWorker (CThreadWorker * worker ); 16 static int WorkerIdleTimeout (CThreadPool * pool); 17 static CThreadWorker * GetWorker (CThreadPool * pool, void (* func) (void * arg), void * arg ); 18 static void WakeupWorkerThread (CThreadWorker * worker); 19 20 int StartWork (CThreadPool * Pool, void (* func) (void * arg), void * arg); 21 CThreadPool * CreateThreadPool (unsigned int min, unsigned int max ); 22 23 CThreadPool * CreateThreadPool (unsigned int min, unsigned int max) 24 {25 CThreadPool * poo; 26 27 pool = (CThreadPool *) malloc (sizeof (CThreadPool )); 28 29 if (pool = NULL) {30 return NULL; 31} 32 33 pool-> head _ = NULL; 34 pool-> min _ = min; 35 pool-> cur _ = 0; 36 pool-> max _ = max; 37 pool-> last_empty _ = time (NULL); 38 pthread_mutex_init (& pool-> mutex _, NULL); 39 40 int ret = 0; 41 while (min --) {42 ret = CreateOneThread (pool); 43 if (ret! = 0) {44 exit (0); 45} 46} 47 return pool; 48} 49 50 static int CreateOneThread (CThreadPool * pool) 51 {52 pthread_t tid; 53 return pthread_create (& tid, NULL, DoProcess, pool); 54} 55 56 static void * DoProcess (void * arg) 57 {58 CThreadPool * pool = (CThreadPool *) arg; 59 60 CThreadWorker worker; 61 62 InitWorker (& worker); 63 64 pthread_mutex_lock (& pool-> mutex _); 65 pool-> cur _ + = 1; 66 67 (; ;) {68 PushWork (pool, & worker); 69 worker. sleep_when _ = time (NULL); 70 pthread_mutex_unlock (& pool-> mutex _); 71 72 pthread_mutex_lock (& worker. mutex _); 73 while (worker. wake_up _! = 1) {74 pthread_cond_wait (& worker. wake _, & worker. mutex _); 75} 76 // The worker thread has been awakened. Prepare to start the task and modify the wake_up _ flag. 77 worker. wake_up _ = 0; 78 pthread_mutex_unlock (& worker. mutex _); 79 80 // run our task. After the task is completed, modify worker. func to NULL. 81 worker. func (arg); 82 worker. func = NULL; 83 84 // After the task is executed, the thread pool should judge based on the current thread pool status to re-Add the thread to the thread pool, you still need to create a new thread. 85 pthread_mutex_lock (& pool-> mutex _); 86 if (WaitWorkerTimeout (pool) & NoThreadInPool (pool) & CanCreateThread (pool )) {87 // when we execute this task, other tasks wait for Idle threads for more than 1 second, and there is no thread in the thread pool, the number of threads in the thread pool does not exceed the maximum number of threads allowed to be created 88 CreateOneThread (pool); 89} 90 91 // No threads exist in the thread pool, re-add this thread to the thread pool 92 if (NoThreadInPool (pool) {93 continue; 94} 95 96 // The number of threads in the thread pool is below the minimum threshold, re-add this thread to the thread pool 97 if (pool-> curr <= pool-> min) {98 continue; 99} 100 101 // The thread's sleeping time exceeds 1 second, indicating that the thread pool is not very busy. You do not need to add the thread back to the thread pool 102 if (WorkerIdleTimeout (pool) {103 break; 104} 105} 106 107 pool-> cur-= 1; 108 pthread_mutex_unlock (& pool-> mutex); 109 110 pthread_cond_destroy (& worker. 111 pthread_mutex_destroy (& worker. mutex _); 112 113 pthread_exit (NULL); 114} 115 static void InitWorker (CThreadWorker * worker) 116 {117 worker-> next _ = NULL; 119 worker-> wake_up _ = 0; 120 pthread_cond _ Init (& worker-> wake _, NULL); 121 pthread_mutex_init (& worker-> mutex _, NULL); 122 worker-> tid _ = pthread_self (); 123 worker-> func = NULL; 124 worker-> arg = NULL; 125 worker-> sleep_when _ = 0; 126} 127 128 static void PushWork (CThreadPool * pool, CThreadWorker * worker) 129 {130 worker-> next _ = pool-> head _; 131 pool-> next _ = worker; 132} 133 134 static int WorkerIdleTimeout (CThreadPool * pool) 135 {136 CThreadWork Er * worker; 137 138 if (NoThreadInPool (pool) {139 return 0; 140} 141 worker = pool-> head _; 142 return (time (NULL)> worker-> sleep_when _ + 1 )? 1: 0; 143} 144 145 int StartWork (CThreadPool * pool, void (* func) (void * arg), void * arg) 146 {147 if (func = NULL) {148 return-1; 149} 150 151 CThreadWorker * worker; 152 pthread_mutex_lock (& pool-> mutex _); 153 worker = GetWorker (pool, func, arg ); 154 pthread_mutex_unlock (& pool-> mutex _); 155 156 if (worker = NULL) {157 return-2; 158} 159 160 WakeupWorkerThread (worker); 161 return 0; 162} 163 164 static CThreadWorker * GetWorker (CThreadPool * pool, void (* func) (void * arg), void * arg) 165 {166 CThreadWorker * worker; 167 168 if (NoThreadInPool (pool) {169 return NULL; 170} 171 172 worker = pool-> head _; 173 PopWork (pool, worker ); 174 175 if (NoThreadInPool (pool) {176 pool-> last_empty _ = time (NULL); 177} 178 179 worker-> func = func; 180 worker-> arg = arg; 181 182 return worker; 183} 184 185 static void PopWork (CThreadPool * pool, CThreadWorker * worker) 186 {187 pool-> head _ = worker-> next _; 188 worker-> next _ = NULL; 189} 190 191 static void WakeupWorkerThread (CThreadWorker * worker) 192 {193 pthread_mutex_lock (& worker-> mutex _); 194 worker-> wake_up _ = 1; 195 pthread_mutex_unlock (& worker-> mutex _); 196 197 pthread_cond_signal (& worker-> wake _); 198}
3. Conclusion
This thread pool model implements some actual application scenarios in our online environment. The following issues are not considered:
1. without considering the concept of a task class, write a task base class, and then the specific task class inherits this base class to implement its own functions. during actual execution, only add_task is required, add the task to the thread pool. This aims to send a signal to each task and determine whether to terminate the task execution. The current thread pool cannot do this, nor can it determine whether the task execution time is time-out.
2. Each thread executes only one task, instead of a task queue, to execute the tasks in the task queue. I am not sure about this. What are the advantages of task queue.
What I can think of is this. I don't know what scenarios need to be considered when you are working in a specific application thread pool. What I think of here is 1. Flexible termination of tasks; 2. You can determine whether the task times out. 3. You can perform load balancing on the tasks to be executed. (this is not a problem in the thread pool, in the extreme case, there are a bunch of tasks, and there are not enough threads in the thread pool. This problem does exist ).
I can solve these problems by myself. I Can slightly modify the above thread pool, and add a task queue in each thread result ThreadWorker. When there is no task, the thread is blocked, otherwise, the task is taken out for execution.
On the first point, how can we modify this framework to send signals to tasks.
Second, determine whether the task times out?
Welcome to join us!
A windows C ++ thread pool code is required. It is better to have a test code.
There are many computer languages. Generally, they can be divided into three categories: machine language, assembly language, and advanced language.
Each operation and step of a computer is performed by a program compiled in computer language. A program is a set of commands to be executed by a computer, all programs are written in the language we know. Therefore, people must send commands to computers through computer languages to control computers.
The computer can recognize only machine languages, that is, code consisting of 0 and 1. But usually people do not use machine language when programming, because it is very difficult to remember and recognize.
Currently, there are two common programming languages: assembly language and advanced language.
The essence of an assembly language is the same as that of a machine language. It directly performs hardware operations. However, commands use identifiers abbreviated in English to facilitate identification and memory. It also requires programmers to write the specific operations in each step in the form of commands. An assembler is generally composed of three parts: commands, pseudo commands, and macro commands. Each instruction of the assembler can only correspond to a very subtle action in the actual operation process, such as moving and auto-incrementing. Therefore, the assembler source program is generally lengthy, complex, and error-prone, in addition, the use of assembly language programming requires more computer expertise, but the advantages of assembly language are also obvious, the operations that can be done by assembly language are not implemented by General high-level languages, in addition, the executable files generated by the source program assembly are not only small, but also fast.
Advanced languages are the choice of most programmers. Compared with the assembly language, it not only combines many related machine commands into a single command, but also removes details related to specific operations but irrelevant to the completion of the work, such as the use of stacks, registers, etc, this greatly simplifies the instructions in the program. At the same time, programmers do not need to have too much professional knowledge because many details are omitted.
Advanced languages are not specific to a specific language, but include many programming languages, such as VB, VC, FoxPro, and Delphi, the syntax and command formats of these languages are different.
Programs compiled in advanced languages cannot be directly recognized by computers and can be executed only after conversion. They can be divided into two types by conversion method:
Interpretation: The execution method is similar to "simultaneous translation" in our daily life. The source code of an application is translated into the target code (machine language) by the interpreter of the corresponding language, therefore, the efficiency is relatively low, and it is not possible to generate executable files that can be executed independently. The application cannot be separated from its interpreter. However, this method is flexible and can be dynamically adjusted and modified.
Compilation class: Compile refers to translating the program source code into the target code (machine language) before the application source code is executed. Therefore, the target program can be executed independently from the language environment, it is easy to use and efficient. However, once the application needs to be modified, it must first modify the source code, and then re-compile and generate a new target file (*. OBJ) for execution. Only the target file without the source code can be modified, which is inconvenient. Currently, most programming languages are compiled, such as Visual C ++, Visual Foxpro, and Delphi.
[NextPage]
Start from learning Programming
If you want to learn programming but do not know where to start, you may wish to take a look at the following learning solutions, which may give you some inspiration!
========================================================== ======
Solution 1: Basic Language & Visual Basic
Advantages
(1) Basic is easy to learn and use.
(2) Visual Basic provides powerful Visual programming capabilities, allowing you to easily make beautiful programs.
(3) numerous controls make programming as simple as building blocks.
(4) All the localization of Visual Basic makes us the most popular English speaker.
Disadvantages
(1) Visual Basic is not a real object-oriented development stationery.
(2) There are too few data types in Visual Basic and pointer is not supported, which makes it very expressive.
(3) Visual Basic does not... the remaining full text>
How to use multithreading in easy language?
When using multithreading in easy language, you will find some unstable phenomena, such as: unstable program running, errors reported when you exit the program, crashes, and inability to directly destroy components in multiple threads. For example, if you run the following code, the program automatically exits .. Subroutine _ button 1 _ clicked
Startup thread (& multithreading subroutine,). subroutine multithreading subroutine
Button 1. Destroy () 'is useless! Now we can change the method of calling multithreading and use the tag feedback event to execute multithreaded subprograms. Most unstable problems can be solved.
The specific Execution Code should be written in the tag feedback event, and the "call feedback event" of the tag will be used to call the event in multiple threads. It is easy to perform special processing on the feedback events of this tag component.
The above code can be written as follows, and you can run it through:. subroutine _ button 1 _ clicked
Startup thread (& multithreading subroutine,). subroutine multithreading subroutine
Tag 1. Call feedback event (0, 0, false). subroutine _ Tag 1 _ feedback event, integer type
. Parameter 1, integer
. Parameter 2, integer
Button 1. Destroy () Note: The following call is incorrect!
Start thread (& _ label 1 _ feedback event ,)
And
. Subroutine multi-thread subroutine
_ Label 1 _ feedback event ()