In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-11 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)06/02 Report--
At present, the popular Windows operating system can run several programs at the same time (independent programs are also called processes). For the same program, it can be divided into several independent execution streams, which we call threads, which provide the ability of multitasking. It is a widely used method to study software from the point of view of process and thread. the emergence of the concept of process and thread is of great significance to improve the parallelism of software. Today's large-scale applications are all multi-threaded and multitasking, and single-threaded software is unthinkable. Therefore, it is necessary for every programmer to master the method of multithreading and multitask design. This example discusses the problems often encountered in the application of multi-thread technology, such as communication and synchronization between threads, and uses multi-thread technology to communicate between threads to realize the simple sorting of numbers.
I. implementation of multithreaded programming guidelines
1. Understand threads
To explain threads, we have to talk about processes, which are execution instances of applications, and each process consists of private virtual address space, code, data, and other system resources. The resources created by the process at run time die as the process terminates. The basic idea of thread is very simple, it is an independent execution flow, an independent execution unit within the process, equivalent to a subroutine, which corresponds to the CwinThread class object in Visual C++. When a single executor runs, it contains a main thread by default, which appears in the form of a function address and provides the startup point of the program, such as the main () or WinMain () function. When the main thread terminates, the process terminates. According to the actual needs, the application can be divided into many independent threads, each running in parallel in the same process.
All threads in a process are in the process's virtual address space, using the process's global variables and system resources. The operating system assigns different CPU time slices to each thread. At a certain time, CPU only executes threads in one time slice, and the corresponding threads in multiple time slices take turns in the CPU. Because each time slice is very short, it seems to the user that each thread is processed in parallel in the computer. The operating system arranges the CPU according to the priority of the thread. The high priority thread runs first, while the low priority thread continues to wait.
Threads are divided into two types: user interface threads and worker threads (also known as background threads). User interface threads are usually used to process user input and respond to various events and messages. In fact, the CWinAPP object, the main execution thread of an application, is a user interface thread that is created and started automatically when the application starts. Similarly, its termination also means the end of the program and the termination of the process. The worker thread is used to perform the background processing tasks of the program, such as calculation, scheduling, reading and writing to the serial port, etc., the difference between it and the user interface thread is that it does not need to be created from the CWinThread class, the most important thing for it is how to implement the running control function of the worker thread task. Worker threads and user interface threads call different versions of the same function when they start; finally, the reader needs to understand that all threads in a process share the variables of their parent process, but at the same time each thread can have its own variables.
II. Multithreaded programming Guide Thread Management and Operation
1. Start the thread
To create a user interface thread, you must first generate a derived class from the class CwinThread, and you must declare and implement the CwinThread derived class using DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE. The second step is to overload some of the derived class's member functions such as ExitInstance (), InitInstance (), OnIdle (), PreTranslateMessage (), and so on, as needed. Finally, a version of the AfxBeginThread () function is called: CWinThread* AfxBeginThread (CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL) to start the user interface thread, where the first parameter is the pointer variable pointing to the defined user interface thread class, the second parameter is the thread's priority, the third parameter is the stack size corresponding to the thread, and the fourth parameter is the additional flag when the thread is created. The default is normal, and if CREATE_SUSPENDED, the thread is suspended after startup.
For a worker thread, to start a thread, you first need to write a function such as Fun1 () that you want to run in parallel with the rest of the application, then define a pointer variable * pThread to the CwinThread object, call the AfxBeginThread (Fun1,param,priority) function, and start the thread to execute the above Fun1 () function at the same time as the return value is assigned to the pThread variable, where Fun1 is the name of the function to be run by the thread It is also the name of the control function mentioned above, param is any 32-bit value to be passed to the thread function Fun1, priority is the priority that defines the thread, it is a predefined constant, readers can refer to MSDN.
2. Priority of the thread
The following member functions of the CwinThread class are used for thread priority operations:
Int GetThreadPriority ()
BOOL SetThradPriority () (int nPriority)
The above two functions are used to obtain and set the priority of the thread, where the priority is relative to the priority level of the thread, the thread with high priority runs first for the thread at the same priority level; for threads at different priority levels, who has the highest priority level will run first. As for the required constants for priority setting, you can refer to MSDN. Note that in order to set the priority of a thread, the thread must have THREAD_SET_INFORMATION access when it is created. The CwinThread class does not provide a function for setting the priority level of a thread, but it can be achieved through the Win32 SDK functions GetPriorityClass () and SetPriorityClass ().
3. Thread suspension and recovery
The CWinThread class contains functions for the application to suspend and resume the thread it created, where SuspendThread () is used to suspend the thread and suspend its execution, and ResumeThread () is used to resume the thread's execution. If you execute SuspendThread () on a thread several times in a row, you need to execute ResumeThread () several times in a row to restore the thread.
4. End thread
There are three ways to terminate a thread: a thread can call AfxEndThread () internally to terminate its own running; it can call BOOL TerminateThread (HANDLE hThread, DWORD dwExitCode) outside the thread to forcibly terminate the operation of a thread, and then call the CloseHandle () function to release the stack occupied by the thread; the third method is to change the global variable so that the thread's execution function returns, then the thread terminates. Taking the third method as an example, some of the code is given below:
/
/ CtestView message handlers
/ Set to True to end thread
A global variable defined by Bool bend=FALSE;// that controls the running of a thread
/ / The Thread Function
UINT ThreadFunction (LPVOID pParam) / / Thread function
{
While (! bend)
{
Beep (100100)
Sleep (1000)
}
Return 0
}
/ / /
CwinThread * pThread
HWND hWnd
Void CtestView::OninitialUpdate ()
{
HWnd=GetSafeHwnd ()
PThread=AfxBeginThread (ThradFunction,hWnd); / / start the thread
PThread- > musebAutoDeleteFALSEX handle / thread for manual deletion
Cview::OnInitialUpdate ()
}
/
Void CtestView::OnDestroy ()
{
Bend=TRUE;// changes variables, thread ends
WaitForSingleObject (pThread- > masked hThreadneedle infinite); / / wait for the thread to finish
Delete pThread;// delete thread
Cview::OnDestroy ()
}
Guidelines for multithreaded programming Communication between threads
Usually, a secondary thread completes a specific type of task for the main thread, which implies that a communication channel needs to be established between the primary thread and the secondary thread. In general, there are several ways to accomplish this communication task: using global variables (which is actually used in the example in the previous section), using event objects, and using messages. Here we mainly introduce the latter two methods.
1. Use user-defined message communication
In Windows programming, each thread of the application has its own message queue, even the worker thread is no exception, which makes it very easy for threads to use messages to transmit information. First, the user defines a user message, as follows: # define WM_USERMSG WMUSER+100; calls:: PostMessage ((HWND) param,WM_USERMSG,0,0) or CwinThread::PostThradMessage () in one thread when needed to send the message to another thread. The four parameters of the above function are the handle of the destination window to which the message is to be sent, the message marker to be sent, and the parameters WPARAM and LPARAM of the message. The following code is a modification of the code in the previous section, and the result of the modification is to display a dialog box at the end of the thread, indicating that the thread ends:
UINT ThreadFunction (LPVOID pParam)
{
While (! bend)
{
Beep (100100)
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 messages to the user interface thread. For the worker thread, if its design pattern is also message-driven, the caller can send it messages such as initialization, exit, and perform some specific processing. Let it finish in the background. In the control function, you can directly use:: GetMessage () this SDK function for message sorting and processing, and implement a message loop by yourself. When the GetMessage () function judges that the message queue of the thread is empty, the thread gives the time slice assigned to it by the system to other threads, which does not take up the time of CPU. If the message queue is not empty, it gets the message, judges the content of the message and processes it accordingly.
2. Use event object to realize communication
A more complex way to communicate between threads is to use an event object, represented by an object of the Cevent class of MFC. The event object is in one of two states: signaled and unsignaled, and the thread can monitor events in the signaled state in order to perform operations on the event at the appropriate time. The code of the above example is modified as follows:
/
Cevent threadStart, threadEnd
UINT ThreadFunction (LPVOID pParam)
{
:: WaitForSingleObject (threadStart.m_hObject,INFINITE)
AfxMessageBox ("Thread start.")
While (! bend)
{
Beep (100100)
Sleep (1000)
Int result=::WaitforSingleObject (threadEnd.m_hObject,0)
/ / wait for the threadEnd event to have a signal. When there is no signal, the thread hovers here.
If (result==Wait_OBJECT_0)
Bend=TRUE
}
:: PostMessage (hWnd,WM_USERMSG,0,0)
Return 0
}
/ / /
Void CtestView::OninitialUpdate ()
{
HWnd=GetSafeHwnd ()
ThreadStart.SetEvent (); / / threadStart event has a signal
PThread=AfxBeginThread (ThreadFunction,hWnd); / / start the thread
PThread- > m_bAutoDelete=FALSE
Cview::OnInitialUpdate ()
}
/
Void CtestView::OnDestroy ()
{
ThreadEnd.SetEvent ()
WaitForSingleObject (pThread- > masked hThreadpender Infinite)
Delete pThread
Cview::OnDestroy ()
}
When you run this program, when you close the program, a prompt box is displayed, showing "Thread ended".
IV. Synchronization between threads in multithreaded programming guidelines
As we mentioned earlier, each thread can access common variables in the process, so the problem to be paid attention to in the process of using multithreading is how to prevent two or more threads from accessing the same data at the same time, so as not to destroy the integrity of the data. Ensuring that threads can properly coordinate their work together is called synchronization between threads. The event object described in the previous section is actually a form of synchronization. Synchronization classes are used in Visual C++ to solve the data insecurity caused by operating system parallelism. The seven multithreaded synchronization classes supported by MFC can be divided into two categories: synchronization objects (CsyncObject, Csemaphore, Cmutex, CcriticalSection and Cevent) and synchronization access objects (CmultiLock and CsingleLock). This section mainly introduces the critical section (critical section), mutual exclusion (mutexe), semaphores (semaphore), these synchronization objects make each thread work together and the program runs more safely.
1. The critical section of the multithreaded programming guide
A critical section is a way to ensure that only one thread can access data at a time. In the process of using it, it is necessary to provide each thread with a shared critical section object, no matter which thread occupies the critical section object, it can access the protected data, and other threads need to wait until the thread releases the critical section object, and after the critical section is released, other threads can seize the critical area in order to access the shared data. The critical section corresponds to a CcriticalSection object, and when the thread needs to access the protected data, the Lock () member function of the critical section object is called; when the operation on the protected data is completed, the Unlock () member function of the critical section object is called to release the ownership of the critical section object so that another thread can seize the critical section object and access the protected data. Start two threads at the same time, and their corresponding functions are WriteThread () and ReadThread (), respectively, to operate on the common array array []. The following code shows how to use the critical section object:
# include "afxmt.h"
Int array [10], destarray [10]
CCriticalSection Section
UINT WriteThread (LPVOID param)
{
Section.Lock ()
For (int xerox length
Int iTunes 1, jacks 0
Long temp
For (iTun1; I {
Temp = Array [I]; / / fetch the first element value of the unordered data after the sequence
For (jfanti; j > 0; jmuri -) / / and the previous ordered data are compared one by one to find the appropriate insertion location.
{
If (Array [j-1] > temp) / / move back if the element is larger than the insertion value
Array [j] = Array [j-1]
Else / / if the element is smaller than the insert value, then the next bit in that position is the position where the element is inserted
Break
}
Array [j] = temp
}
PrintResult (Array, iLength, "Insert Sort"); / / print the sorting results to the console
InterlockedIncrement (& ThreadCompleted); / / add 1 to the thread completion mark before returning
If (ThreadCompleted = = 4) SetEvent (evtTerminate); / / check whether all other threads have finished executing
/ / if all execution is finished, set the program end semaphore
Return 0
}
/ * Quick sorting idea: quick sorting is an application of the divide-and-conquer idea, which first selects a fulcrum, and then exchanges the elements smaller than the fulcrum to the front of the fulcrum, and the elements larger than the fulcrum to the right side of the fulcrum. And then to the left and right of the fulcrum.
The side part is processed in the same way, so that after several times, the data becomes orderly. The following implementation uses recursion
Create two cursors: iLow,iHigh;iLow points to the first element of the sequence, iHigh points to the last first element as the fulcrum, and stores its value in an auxiliary variable. Then the first position becomes empty and other elements can be placed. So move the cursor forward from the element pointed to by iHigh, iHigh looks for an element smaller than the fulcrum, if found, put it in a vacant position (now the first position), and then the iHigh cursor stops moving, when the position pointed to by iHigh is vacant, and then move the iLow cursor to look for elements larger than the fulcrum to place in the vacant position pointed to by iHigh, and so on until iLow and iHigh are equal. Finally, recursion is used to do the same for the left and right parts * /
Int QuickSort (long* Array, int iLow, int iHigh)
{
If (iLow > = iHigh) return 1; / / Recursive end condition
Long pivot = Array [iLow]
Int iLowSaved = iLow, iHighSaved = iHigh; / / keep the iLow unchanged, and save the iHue value
While (iLow
< iHigh) { while (Array[iHigh] >= pivot & & iHigh > iLow) / / look for elements larger than the fulcrum
IHigh--
Array [iLow] = Array [iHigh]; / / place the found element in a vacant location
While (Array [iLow] < pivot & & iLow < iHigh) / / looking for elements smaller than the fulcrum
ILow + +
Array [iHigh] = Array [iLow]; / / place the found element in a vacant location
}
Array [iLow] = pivot; / / place the fulcrum value to the fulcrum position, when the fulcrum position is vacant
/ / A pair of left and right parts are processed recursively.
QuickSort (Array, iLowSaved, iHigh-1)
QuickSort (Array, iLow+1, iHighSaved)
Return 0
}
/ / each thread uses this function for output, and there is only one monitor, which generates multiple threads.
/ / compete for the right to use the console.
Void PrintResult (long* Array, int iLength, const char* HeadStr)
{
WaitForSingleObject (evtPrint, INFINITE); / / wait for the event to be signaled
/ / EnterCriticalSection (& csPrint); / / Mark a thread to enter the critical section
/ / WaitForSingleObject (mtxPrint, INFINITE); / / wait for mutex to be left empty (no thread owns it)
Int i
Printf ("% s:", HeadStr)
For (iTun0; I {
Printf ("% d,", Array [I])
Sleep (100); / / delay (can be removed)
/ * it just makes it easier to see the problem of multithreaded access to the critical area
If you comment out the statements of critical control, the output will become very messy, and the results of each sort will
Add / drop interval output, if there is no delay, it is not easy to see the result of not controlling the critical region.
, /
}
Printf ("% d\ n", Array [I])
SetEvent (evtPrint); / / restore the event semaphore into a signal
}
IV. Summary
For complex applications, the application of threads provides applications with efficient, fast and secure data processing capabilities. This example describes the problems often encountered in threading, hoping to give some help to readers and play an important role.
Original address: http://xiaocainiao.baijia.baidu.com/article/802175
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.