WINDOWS multi-thread multi-task design preliminary

xiaoxiao2021-03-06  19

Author: Liu Tao Quoted yesky [Introduction:] popular Windows operating system, it can run several programs (also known as programs run independently of the process), for the same program, it can be divided into several independent execution Flow, we call it thread, thread provides multi-tasking ability. Research software is the general purpose of today's general methods, process and threads in today's general, and important significance for improving the parallelity of software. Now the application software is not a multi-thread multi-task handling, the software of single-line city is unimaginable. Therefore, mastered multi-thread multi-task design methods to each programmer must be mastered. This paper is aimed at many problems that are often encountered in applications, such as communication, synchronization, etc., and discuss them, such as communication, synchronization, etc. of threads. First, understand the thread to explain the thread, have to say the process, the process is an executive example of the application, each process consists of private virtual address space, code, data, and other system resources. The resources created at runtime died as the process terminates. The basic idea of ​​thread is very simple, it is a separate executive stream, a separate execution unit inside the process, equivalent to a subroutine, which corresponds to the object of the CwinThread class in Visual C . When an executing program is run, the default running contains a main thread, the main thread is in the form of a function address, such as the main or winmain function, the boot point of the program, when the main thread is terminated, the process also terminates, but according to the process It is needed, the application can break down into a number of independent execution threads, and each thread is run in parallel in the same process. All threads in a process are in the virtual address space of the process, using the global variables and system resources of the process. The operating system assigns different CPU time films to each thread, at a time, the CPU only executes the thread in a time film, and the corresponding thread in multiple time filters is performed in the CPU, because each time the tablet time is very short. Therefore, for the user, it seems to be parallel in the computer in the computer. The operating system arranges the time of the CPU according to the priority of the thread, the priority thread is prioritized, and the priority low-priority thread continues to wait. Threads are divided into two: user interface threads and working threads (also known as background threads). The user interface thread is usually used to handle the user's input and respond to various events and messages. In fact, the application's main execution thread CWINAPP object is a user interface thread, and automatically creates and starts automatically when the application starts, the same termination means At the end of the program, the city is terminated. Worker threads are used to execute the background processing tasks, such as calculation, scheduling, read / write operations such as serial ports, and the difference between it and the user interface thread is not to be created from the CWINTHREAD class. The most important thing for it is How to implement the running control function of working thread tasks. Working thread and user interface thread starts to call different versions of the same function; finally need readers to understand that all threads in a process share their parent's variables, but at the same time each thread can have their own variables. Second, the management and operation of threads 1. The thread starts to create a user interface thread, first generate a derived class from class CWINTHREAD, and you must use Declare_DyncReate and Implement_DyncReate to declare and implement this CWINTHREAD derived class.

The second step is to overload some member functions of the school, such as: exitInstance (); initInstance (); onIdle (); pretedMessage (), etc., start the user interface thread, call a version of the AFXBEGINTHREAD () function. : CWinThread * AfxBeginThread (CRuntimeClass * pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL); wherein the first parameter is a point defined by the user interface thread class pointer variable, the second parameter For the priority of the thread, the third parameter is the stack size corresponding to the thread, the fourth parameter is the additional flag of thread creation, the default is normal, such as the thread starts after CREATE_SUSPENDED. For the working thread, start a thread, first need to write a function that is hoped with the rest of the application in parallel, such as fun1 (), then defines a pointer variable pointing to the CWINTHREAD object * pthread, call AfxBeginthread (Fun1, Param, Priority The function, the return value pays to the PTHREAD variable while starting the thread to perform the above FUN1 () function, where fun1 is the name of the function to run, and is both the name of the control function mentioned above, Param is ready Any 32-bit value transmitted to the thread function FUN1, Priority is the priority of the thread that is a predefined constant, and the reader can refer to MSDN. 2. The thread's priority The following CWINTHREAD class member functions are used for thread priority:

INT getthreadPriority (); Bool setthradpriority () (int nPriority); The above two functions are used to obtain and set the priority of the thread, and the priority here is the priority level relative to the thread. Threads in the same priority level, high priority threads are first run; at different priority levels, who is high, who is high, who runs first. As for the constant required for priority settings, you can refer to MSDN, you have to pay attention to the priority of the thread, this thread must have truth_set_information access. For the setting of the priority level of the thread, the CWINTHREAD class does not provide the corresponding function, but can be implemented by the Win32 SDK function getPriorityClass () and setPriorityClass (). 3. The hanging of threads, the recovery CWINTHREAD class contains a function of the application suspension and recovery it creates, where suspendthread () is used to suspend the thread, suspend the execution of the thread; ResumeThread () is used to resume the execution of the thread. If you execute SuspendThread () for a thread, you need to continuously perform the corresponding RESUMETHREAD () to restore the running of the thread. 4. End the thread termination thread has three ways, threads can call AFXendThread () inside itself to terminate itself; you can call Bool TerminateThread (Handle Hthread, DWORD DWORD DWEXITCODE) on the outside of the thread to force a thread run, and then call CloseHandle. The stack occupied by the function release thread; the third method is to change the global variable to return the execution function of the thread, and the thread is terminated. The following is a third method as an example, give some code:

// ctestView message handlers / set to true to end threadBool bend = false; // defined global variable for controlling the run // the thread functionuint threadfunction (lpvoid pParam) // thread function {while (! "{ Beep (100,100); Sleep (1000);} return 0;} CwinThread * pThread; HWND hWnd; / Void CtestView :: OninitialUpdate () {hWnd = GetSafeHwnd (); pThread = AfxBeginThread (ThradFunction, hWnd); // start threads pThread-> m_bAutoDelete = FALSE; // thread manually remove Cview :: OnInitialUpdate ();} Void CtestView :: OnDestroy () {bend = TRUE; // change the variable, the thread end WaitForSingleObject (pThread-> m_hThread, INFINITE); // Waiting for the thread ends delete pthread; // Delete thread cView :: ONDESTROY ();} III communication Normally, a secondary thread must perform a particular type of task for the primary thread, which hidden There is a channel that represents a communication between the main thread and the secondary thread. In general, there are several ways to implement this communication task: use global variables (the example in the previous section is actually used), using event objects, use messages. Here we mainly introduce the latter two methods. 1. Using user-defined messaging in Windows programming, every thread of the application has its own message queue, and even the working thread is no exception, so that the thread uses the message to pass the information. simple. First, users want to define a user message, as shown below: #Define WM_USERMSG WMUSER 100; In need, call :: PostMessage ((hwnd) Param, WM_USERMSG, 0, 0) or CWINTHREAD :: PostthradMessage The message is sent to another thread. The four parameters of the above functions are the handle of the destination window to be sent, the message flag, the parameter WPARAM and LParam of the message. The following code is a modification of the previous codes, the modified result is to display a dialog at the end of the thread, prompting the thread end:

Uint threadfunction (lpvoid pParam) {while (! "{Beep (100, 100); Sleep (1000);} :: postmessage (hwnd, wm_usermsg, 0, 0); Return 0;} WM_USERMSG message response function is ONTHREADEDED (WPARAM WPARAM, LPARAM LPARAM) Long ctestview :: ONTHREADENDED (WPARAM WPARAM, LPARAM LPARAM) {AFXMESSAGEBOX ("thread ended."); retrun 0;} The above example is the worker thread to send messages to the user interface, for worker threads, If its design mode is also a message driver, the caller can send initialization, exit, exit, perform some specific processing, etc., let it complete in the background. You can use it directly in the control function :: getMessage () this SDK function to perform message snualing and processing, implement a message loop yourself. When the message queue that determines the thread, the thread is assigned to it to other threads. If the message queue is not empty, if the message queue is not empty, get this message, judgment The content of this message is processed accordingly. 2. The method of implementing communication with event objects to transmit signals between threads is complicated to communicate with event objects, expressed with objects of the MFC's CEVENT class. Event objects are one of two states: there are signals and no signals, and threads can monitor events with signal status so as to perform operations on events when appropriate. The above example code is modified as follows:

Cevent threadStart, threadEnd; UINT ThreadFunction (LPVOID pParam) {:: WaitForSingleObject (threadStart.m_hObject, INFINITE); AfxMessageBox ( "Thread start."); While (bend!) {Beep (100,100); Sleep (1000); Int result =: WaitForsingleObject (threadend.m_hobject, 0); // Waiting for the Thieadend event has a signal, no signal When the thread is here hovering if (Result == Wait_Object_0) bend = true;}: postMessage (hwnd, wm_usermsg, 0, 0); return 0;} / Void CtestView :: OninitialUpdate () {hWnd = GetSafeHwnd (); threadStart.SetEvent (); // threadStart signal events pThread = AfxBeginThread (ThreadFunction, hWnd); // start threads pThread-> m_bAutoDelete = FALSE; Cview :: OnInitialUpdate ();} Void CtestView :: OnDestroy () {threadEnd.SetEvent (); WaitForSingleObject (pThread-> m_hThread, INFINITE); delete pThread; Cview :: OnDestroy ();} run this program When the program is turned off, the prompt box is displayed, showing "Thread ended" four, before we speak, each thread can access public variables in the process, so the problem that needs attention during the use of multi-threaded procedures is How to prevent two or more threads from accessing the same data simultaneously to avoid damaging the integrity of the data. Ensure that each thread can be suitably coordinated as synchronization between threads. The event object introduced in the previous section is actually a synchronization form. Using synchronous classes in Visual C to solve data unsafe problems caused by the operational system, the seven multi-threaded synchronization classes supported by the MFC can be divided into two categories: synchronous objects (CsyncObject, CSemaphore, CMutex, ccriticalsection and cevent ) And synchronize access objects (CMULTILOCK and CSILOCK). This section mainly introduces critical section, mutexe, semaphore, which makes each thread coordinated work, and the program is safer. 1. The critical area of ​​the critical area is to ensure that only one thread can access data at a certain time. In the process of using it, you need to provide a shared critical area object to each thread. No matter which thread occupies the critical area object, you can access the protected data. At this time, the other thread needs to wait until the thread releases the critical area object. After the critical area is released, the additional thread can strongly account for this critical area to access shared data. The critical area corresponds to a ccriticalSECTION object. When the thread needs to access the protection data, the Lock () member function of the critical area object is invoked; when the operation of the protection data is completed, the Unlock () member function of the critical area object is released to the critical area. The ownership of the object so that another thread can capture critical area objects and access protected data.

At the same time, start two threads, and their corresponding functions are WriteThread () and readthread () to operate on public array array [], the following code illustrates how to use critical area objects: #include "afxmt.h" int "INT Array [10], Destarray [10]; ccriticalsection section; uint WriteThread (LPVOID param) {section.lock (); for (int x = 0; x <10; x ) array [x] = x; sectionunlock ); Uint readthread (lpvoid param) {section.lock (); for (int x = 0; x <10; x ) destarray [x] = array [x]; sectionUnlock ();} The above code is running The result should be that the elements in the Destarray array are 1-9, rather than messy numbers. If you don't use synchronization, it is not this result, and interested readers can experiment. 2. Mutual exclusion is similar to the critical region, but it is relatively complicated when used, but it can simultaneously achieve synchronization between the same application thread, but also achieve synchronization between different processes, thereby achieving security sharing of resources. Mutual exclusion corresponds to the CMUTEX class, when using a mutex, you must create a CSILOCK or CMULTILOCK object for actual access control because the examples here only handle a single mutual exclusion, so we can use CSILOCK objects, this object The LOCK () function is used to occupy mutual exclusion, and unlock () is used to release mutual exclusion. The implementation code is as follows:

#include "afxmt.h" int Array [10], Destarray [10]; cmutex section; / uint WriteThread (LPVOID param) {csinglelock singlelock; singleelock.lock (); for (int x = 0; X <10; x ) array [x] = x; SingLock.unlock ();} uint readthread (lpvoid param) {csinglelock singlerock; singleelock (& ​​section); Singlerock.lock (); for (int x = 0; x < 10; x ) destarray [x] = array [x]; singleLock.unlock ();} 3. The usage of the semaphore signal is similar, and the mutual use is similar. It can be used to allow multiple threads to access the same resource at the same time. Creating a semaphore class declares an object, once a semaphore object is created, It is possible to use it to access the resources. To implement the counting process, create a CSILOCK or CMLTILock object, then use the LOCK () function of the object to reduce the count value of this session, unlock () is reversed. The following code initiates three threads, executes the two message boxes, and then the third message box after 10 seconds is displayed.

转载请注明原文地址:https://www.9cbs.com/read-43897.html

New Post(0)