[Foreword:] The current popular Windows operating system, it can run several programs at the same time (independently running program is also known as the process), for the same program, it can be divided into several separate executives, we call it Threads, threads provide multiple task processing capabilities. 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 thread
To explain the thread, you 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 thread
1. Thread start
Create a user interface thread, first generate a derived class from class CWINTHREAD, and you must declare and implement this CwinThread derived class using declare_dyncreate and import_dyncReate.
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. Thread priority
The following CWINTHREAD class member functions are used for thread priority:
INT getthreadpriority ();
Bool setthradpriority () (int nPriority);
The two functions described above are used to obtain and set the priority of the thread. The priority here is a thread in the same priority level relative to the priority level of the thread, the priority is high. Run; in different priority levels, who is high, who is high, and who will run. 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. Thread hanging, recovery
The CWINTHREAD class contains a function of the application suspension and recovery it created, 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 thread
There are three ways to terminate thread, and threads can call AFXendThread () to terminate itself inside; can call Bool TerminateThread (Handle Hthread, DWORD DWORD DWEXITCODE) outside the thread to force a thread run, and then call the closehandle () function. The stack occupied by the 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 an example, give some code: // CTestView Message Handlers
/ Set to true to end thread
Bool bend = false; // defined global variable for controlling the operation of threads
// THREAD FUNCTION
UINT ThreadFunction (LPVOID PPARAM) // Thread Function
{
While (! bend)
{Beep (100,100);
Sleep (1000);
}
Return 0;
}
CWINTHREAD * PTHREAD;
Hwnd hwnd;
/
Void ctestview :: onInitialupdate ()
{
HWnd = getsafehwnd ();
Pthread = AFXBEGINTHREAD (thradfunction, hwnd); // Start thread
Pthread-> m_bautode = false; // thread to manually delete
CView :: OnInitialupdate ();
}
Void ctestview :: ONDESTROY ()
{bend = true; // change the variable, thread end
WaitforsingleObject (pthread-> m_hthread, infinite); // Waiting for thread end
Delete pthread; // Delete thread
CView :: ONDESTROY ();
}
Third, communication between threads
Typically, a secondary thread wants to complete a particular type of task for the primary thread, which implies a channel indicating 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. Use user-defined messaging
In Windows programming, every thread of the application has its own message queue, and even the work thread is no exception, so that the message is made very simple to deliver information between the threads. First of all, users want to define a user message, as shown below: #define WM_USERMSG WMUSER 100; when needed, call in a thread
:: PostMessage ((HWND) Param, WM_USERMSG, 0, 0)
or
CWINTHREAD :: PostthradMessage ()
To send this message to another thread, the four parameters of the above function are the handle of the message to be sent to the destination window, 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 (! bend)
{
BEEP (100, 100);
Sleep (1000);
}
:: PostMessage (HWND, WM_USERMSG, 0, 0);
Return 0;
}
The response function of the WM_USERMSG message is ONTHREADENDED (WPARAM WPARAM, LPARAM LPARAM) Long Ctestview :: ONTHREADENDED (WPARAM WPARAM, LPARAM LPARAM)
{
AfxMessageBox ("thread ended.");
Retrun 0;
}
The above example is that the worker thread sends a message to the user interface, for the worker thread, if its design mode is a message driver, then the caller can send initialization, exit, execute some specific processing, etc. It is completed 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. Communication with event objects
The method of transmitting signals between threads is complicated to communicate with an event object, represented by an object 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 results = :: waitforsingleObject (threadend.m_hobject, 0);
// Waiting for the ThreadEND event has a signal, no signal is hover here
If (Result == Wait_Object_0)
Bend = true;
}
:: PostMessage (HWND, WM_USERMSG, 0, 0);
Return 0;
}
/
Void ctestview :: onInitialupdate ()
{
HWnd = getsafehwnd ();
Threadstart.sevent (); // ThreadStart event has a signal
Pthread = AFXBEGINTHREAD (ThreadFunction, hwnd); // Start thread
Pthread-> m_bautodelete = false;
CView :: OnInitialupdate ();
}
Void ctestview :: ONDESTROY ()
{threadenD.sevent ();
WaitforsingleObject (pthread-> m_hthread, infinite);
Delete pthread;
CView :: ONDESTROY ();
}
Run this program, when you turn the program, the prompt box is displayed, showing "thread ended"
Fourth, the synchronization between threads
As we said, each thread can access public variables in the process, so the problem that needs to be noted in the process of multithreading is how to prevent two or more threads from accessing the same data simultaneously to avoid damaging data integrity. 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. Critical area
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 (), respectively, to operate on public array array [], the following code illustrates how to use critical area objects:
#include "afxmt.h"
Int Array [10], destarray [10];
CcriticalSection Section;
Uint WriteThread (LPVOID Param)
{Section.lock ();
For (int x = 0; x <10; x )
Array [x] = x;
Section.unlock ();
}
Uint Readthread (LPVOID param)
{
Section.lock ();
For (int x = 0; x <10; x )
Destarray [x] = array [x];
Section.unlock ();
}
The result of the above code should be that the elements in the destarray array are 1-9, rather than messy number, if not using synchronization, is not this result, interested readers can experiment.
2. Mutually exclusive
Mutual exclusion is very similar to the critical region, but it is relatively complex when used, but it can not only achieve synchronization between the same application thread, but also achieve synchronization between different processes, thereby achieving resource security sharing. 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)
{Csingelock singlelock;
SINGLOCK (& section);
Singlock.lock ();
For (int x = 0; x <10; x )
Array [x] = x;
Singlock.unlock ();
}
Uint Readthread (LPVOID param)
{Csingelock singlelock;
SINGLOCK (& section);
Singlock.lock ();
For (int x = 0; x <10; x )
Destarray [x] = array [x];
Singlock.unlock ();
}
3. signal
The usage of the semaphore is very similar, and it is different that it can allow multiple threads to access the same resource at the same time. Creating a semaphore can declare an object with a CSEMaphore class. Once a semaphore object is created, it can be 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.
/
CSemaphore * Semaphore;
Semaphore = New Csemaphore (2, 2);
HWND HWND = GetSafehWnd ();
AfxBeginthread (ThreadProc1, Hwnd);
AfxBeginthread (ThreadProc2, Hwnd);
AfxBeginthread (ThreadProc3, Hwnd);
//
Uint ThreadProc1 (LPVOID param)
{CSIGLOCK SINGELLOCK (Semaphore);
Singlock.lock ();
Sleep (10000);
:: MessageBox ((hwnd) Param, "Thread1 Had Access", "Thread1", MB_OK;
Return 0;
}
Uint ThreadProc2 (LPVOID param)
{CSIGLOCK SINGELLOCK (Semaphore);
Singlock.lock ();
Sleep (10000);
:: MessageBox ((hwnd) Param, "Thread2 Had Access", "Thread2", MB_OK;
Return 0;
}
Uint ThreadProc3 (LPVOID param)
{CSIGLOCK SINGELLOCK (Semaphore);
Singlock.lock ();
Sleep (10000);
:: MessageBox ((hwnd) Param, "Thread3 Had Access", "Thread3", MB_OK;
Return 0;
}
For complex applications, thread applications provide efficient, fast and secure data processing capabilities. This article tells the problems that are often encountered in the thread, I hope to have a certain help to reader friends.