17.1 Threads
All threads are the same for Windows, but MFC divides the thread area into two types: User Interface (UI) threads (UI) and worker threads (worker threads).
The difference between the two threads is that the UI thread has a message loop and the worker thread does not. The UI thread can create windows and process messages sent to these windows. Worker threads perform background tasks.
17.1.1 Creating worker threads
AfxBeginThread defines two versions: one starts the UI thread and the other initiates the author thread
Cwinthread*pthread=afxbeginthread (Threadfunc,&threadinfo);
Start a worker thread and pass it an application-defined data structure address (&threadinfo) that contains the input to the thread.
ThreadFunc is a "threading function" that executes after the thread has started executing.
AfxBeginThread worker threads can accept 4 additional parameters to specify the priority level of the thread, the stack size, the resulting flag, and the security attributes. The complete prototype of the function is:
cwinthread* AFXAPI AfxBeginThread (
Afx_threadproc Pfnthreadproc,
LPVOID Pparam,
int npriority = Thread_priority_normal,
UINT nstacksize = 0,
DWORD dwcreateflags = 0,
Lpsecurity_attributes lpsecurityattrs = NULL);
NPRIORITY specifies the priority level of thread execution. Cwinthread::setthreadpriority can modify the priority level of a thread at any time.
The Nstacksize parameter formulates the maximum stack size for the thread. A value of 0 increases the default stack to 1MB size.
Dwcreateflags default 0 The high-speed system immediately begins execution off-the-shelf. If the create_suspended thread is specified to start, it is in a paused state. It is known that another thread (usually the thread that created it) will not continue until it calls CWinThread::ResumeThread on the thread in the showroom.
For example:
CWinThread *pthread=afxbeginthread (threadfunc,&threadinfo,thread_priority_normal,0,create_suspendend);
.......
Pthread->resumethread ();//start the thread
The last parameter, Lpsecurityattrs, is a pointer to the securtty_attributes structure that formulates the security properties of the new thread and tells the system whether the process inherits the thread handle. The default value of NULL means that the new thread has the same properties as the thread that created it.
Thread functions
A thread function is a callback function, so it must be a static member function or a global function declared outside the class. The prototype is as follows
UINT ThreadFunc (LPVOID pparam);
It is legal to use thread functions for more than two threads, but you should be careful about the re-entry problem due to global variables and static functions, as long as the variables and objects used by the thread are created on the stack, there will be no reentry problems.
Note: callback function: The callback function is a function called through a function pointer. If you pass the pointer (address) of the function as an argument to another function, when the pointer is used to invoke the function it points to, we say this is a callback function. A callback function is not called directly by the implementation method of the function, but is invoked by another party when a particular event or condition occurs, and is used to respond to the event or condition.
Re-entry: The Reentrant function is mainly used in multitasking environments, a reentrant function is simply a function that can be interrupted, that is, it can be interrupted at any moment of execution of the function, go to the OS Scheduler to execute another piece of code, and return control without error The non-reentrant function can be problematic if it is interrupted because it uses some system resources, such as the global variable area, the interrupt vector table, and so on, which cannot be run in a multitasking environment.
17.1.2 Creating a UI thread
Creating a UI thread has a distinct process from creating a worker thread. Worker threads are defined by thread functions, and the behavior of the UI thread is derived from CWinThread dynamically-created classes that are similar to application classes derived from Cwinap.
classCMainWindow: Publiccframewnd{ Public: CMainWindow ();protected: afx_msgvoidOnLButtonDown (Uint,cpoint); Declare_message_map ()}; Begin_message_map (Cmainwindow,cframewnd) On_wm_lbuttondown () End_message_map () Cmainwindow::cmainwindow () {Create ( null,_t ("UI Thread Window"));}voidCmainwindow::onlbuttondown (UINT nflags,cpoint point) {PostMessage (wm_close,0,0);}//The Cuithread classclassCuithread: Publiccwinthread{declare_dyncreate (cuithread) Public: VirtualBOOL InitInstance ();};i Mplement_dyncreate (Cuithread,cwinthread) BOOL cuithread::initinstance () {m_pMainWnd=NewCMainWindow; m_pMainWnd-ShowWindow (sw_show); m_pMainWnd-UpdateWindow (); returnTRUE;}
cwinthread* Pthread=afxbeginthread (Runtime_class (Cuithread));
17.1.3 pausing and continuing the execution of threads
The running thread can be paused with cwinthread::suspendthread, then with CWinThread:; ResumeThread continues to execute.
For each thread, the system maintains a number of pauses, with a value of SuspendThread plus 1,resumethread minus 1. The processor time is only dispatched to the thread if its stack topness is 0 o'clock.
Without the create_suspended flag, the number of thread pauses created is 0, and the thread created with the CREATE_SUSPENDED flag starts with a pause of 1.
Both SuspendThread and ResumeThread return the number of previous pauses on the thread, so that you can ensure that the thread continues to execute. The resumethread can be called repeatedly, regardless of the number of pauses, until the period returns a value of 1. If the current thread is not paused, ResumeThread returns a value of 0.
17.1.4 Using Thread sleep
:: Sleep (0) is used to discard the remaining thread time slices.
:: The value of sleep does not guarantee that a thread will wake up at a precise moment after the specified interval has elapsed. To:: Sleep passing a value of 10000 only guarantees that the thread will wake up at some point after 10 seconds. This depends on the operating system.
17.1.5 terminating a thread
After the thread has started executing, there are two ways to terminate it. The worker thread ends when the thread function executes the return statement, or any function calls AfxEndThread anywhere in the thread.
The UI thread ends when a wm_quit message is sent to its message queue, or if the thread calls AfxEndThread itself. Using API functions::P Ostquitmessage,ui thread can send itself a wm_quit message.
Afxendthread\: Both the:P Ostquitmessage and return receive a 32-bit exit code that can be retrieved with GetExitCodeThread after the type I press is complete.
DWORD Dwexitcode;
:: GetExitCodeThread (Pthread->m_hthread,&dwexitcode);
If the function is called on the executing thread dwexitcode is set to Still_active (0x103)
17.1.6 Automatically delete CWinThread
The statement that terminates the thread above will cause an accident because the closed thread will delete the CWinThread object
There are two workarounds, the first of which is to set the M_bautodelete data member to False to prevent MFC from deleting the CWinThread object. The default value is true to allow automatic deletion. If you use this method, remember to call Delete with the CWinThread pointer returned by AfxBeginThread, or your application may not run because of insufficient memory.
CWinThread *pthread= AfxBeginThread (Threadfunc,null, Thread_priority_normal, 0 ,create_suspended);p thread - >m_bautodelete=false;pthread -> ResumeThread (); // sometime later dword dwexitcode;::getexitcodethread (pThread ->m_hthread,&dwexitcode); if (Dwexitcode==still_active) {} else { PThread;}
The second workaround is to allow CWinThread to perform automatic deletions, but to use Win32::D uplicatehandle function to create a copy of the county handle. Thread sickness to make a reference count, use:: DuplicateHandle copying a newly opened thread handle increases the number of references from 1 to 2.
So when CWinThread's destructor is called:: CloseHandle, the handle is not actually closed; it simply decrements his reference count. The disadvantage of this approach is that you must call CloseHandle yourself to close the handle.
CWinThread *pthread=AfxBeginThread (Threadfunc,null, Thread_priority_normal,0, create_suspended); HANDLE hthread;::D uplicatehandle (GetCurrentProcess (), PThread-M_hthread, GetCurrentProcess (),&hthread,0, false,duplicate_same_access);p thread-ResumeThread ();//sometime laterDWORD dwexitcode;::getexitcodethread (PThread->m_hthread,&dwexitcode);if(dwexitcode==still_active) {}Else{:: CloseHandle (Hthread);}
17.1.7 End Another thread
Here's a more appropriate way to do this:
//Thread ANcontinue =1; CWinThread*pthread=afxbeginthread (threadfunc,&ncontinue);.. HANDLE Hthread=pthread, M_hthread;//Save the thread handle;Ncontinue=0;//Tell thread B to terminate.:: WaitForSingleObject (hthread,infinite);//Thread BUINT ThreadFunc (Lpovid pparam) {int*pcontinue = (int*) Pparam; while(*pcontinue) { // Work work } return 0;}
:: WaitForSingleObject prevents the calling thread from knowing that the specified object (in this case, another thread in this example) enters the "signaled" state. A thread will be signaled at the end.
:: WaitForSingleObject will be returned after the specified time has been exhausted, even if the object has not yet entered the signal-emitting state. You can determine the reason that the function returned by checking the return value. The wait_object_0 means
The object enters the signal-emitting state, and Wait_timeout indicates that it has not yet entered.
By calling:: WaitForSingleObject and setting its wait time to 0, you can quickly determine whether a thread is still running.
if (:: WaitForSingleObject (Hthread,0) = =wait_object_0) {//thethread no longer exists }else{//Thethread is still running.}
This method calls:: WaitForSingleObject will not wait and return directly.
There is also a way to terminate the other thread directly, but only as a last.
:: Termiinatethread (hthread,0);
The end handle is Hthread thread and a release code 0 is paid to him. Some of the Win32 API references include: TerminateThread possible problems, synchronizing objects from orphaned threads to DLLs that do not end properly.
17.1.8 thread, Process priority level
At any point in time, each thread is assigned a priority from 0 to 31. If the priority is 11 of the current car over you are waiting to execute, and all other capital Cpushijian County has a priority of 10 or smaller, then the next
The implementation of the priority is 11 of the current car over you. If a county with two priority 11 is waiting to execute, the scheduler will execute one of the least recently executed.
By law, the scheduler always assigns a time slice to the thread that waits for the current vehicle to have the highest priority in you.
As long as the high-priority worker threads do not monopolize the CPU, even the threads with the lowest priority will get all the time they need (worker threads never pause on Message Queuing because they do not process messages at all).
Initial priority allocation:
When you call AfxBeginThread or cwinthread::setthreadpriority, you specify "relative thread priority." The operating system calculates the "base priority" of the thread, the actual run, combined with the relative priority and the process priority of the owning threads
The priority of the thread (numbering from 0 to 30) is increased or canceled, so it is different at different times. Although you cannot increase the priority of a thread, you can control the base priority by setting the priority type of the process and the relative thread priority level.
Process priority Level type
Most processes start with a priority type of normal_priority_class. But once started, the process can invoke:: Setpriorityclass to modify its priority, which receives the process handle (available:: GetCurrentProcess obtained)
and the parameters given in table 17-1.
Table 17-1 process Priority level types
Priority level Type |
Description |
Idle_priority_class |
The process only runs when the system is idle, for example: No other threads are waiting for a given CPU |
Normal_priority_class |
The default process priority level type. The process does not require special scheduling. |
High_priority_class |
The process receives priority levels above Idle_priority_class and Normal_priority_class |
Realtime_priority_class |
The process must have the highest possible priority, and his thread should have a higher priority than even the thread belonging to the High_priority_class process |
Most applications do not need to modify their priority type. The High_priority_class and realtime_priority_class processes can significantly keep the system responsive and even delay critical system behavior, such as clearing disk high-speed buffers.
One of the legitimate uses of High_priority_class is for system applications, which are hidden for most of the time and only pop up a window when an input time occurs. These applications suspend waiting for the input value to occupy the system with minimal extra
Overhead, but once some type of input appears, they get a higher priority than the average application. Realtime_priority_class is mainly the stomach real-time data acquisition program lockout, in order to be able to make the first work they must share CPU time.
Idle_priority_class is ideal for screen protection, system monitoring, and other low-level applications, which are used primarily to perform some unobtrusive operations in the background.
Line threads to Priority
Table 17-2 gives the values of relative thread precedence, which can be passed to AfxBeginThread and cwinthread::setthreadpriority.
Table 17-2
priority value |
description |
Thread_priority_idle |
If a process has a priority type of high_priority_class or lower, the thread has a base priority of 1, and if the process has a priority of realtime_priority_class, the base priority is 16. |
thread_priority_lowest |
The base priority of the thread is equal to the priority type of the process minus 2 |
Thread_prio Rity_below_normal the base priority of the |
thread is equal to the priority type of the process minus 1 |
thread_priority_normal |
default Thread priority value 。 The base priority of the thread is equal to the priority type of the process |
thread_priority_above_normal |
thread has a base priority equal to the priority type of the process plus 1 |
The base priority of the
thread_priority_highest |
thread equals the priority type of the process plus 2 |
thread_priority_time_critical |
If a process has a priority type of high_priority_class or lower, the thread has a priority of 15. If the priority type of the process is realtime_priority_class, the base priority is. |
The general rule is that if a high priority is required, then the use is usually clear. If the reason for high priority is unclear, then the normal thread priority is available. For most threads, the default thread_priority_normal is the motherland.
But if you are writing an application that uses specialized threads to read and buffer serial ports into the data, unless the relative priority value of the read and buffered threads is thread_priority_highest or thread_priority_time_critical , or it will lose bytes from time to time.
17.1.9 using C Run-time functions in multithreaded applications
Both C and C + + compilers have two versions of runtime libraries: one is thread-safe (can be called by more than two threads) and the other is not. The thread-safe version of the runtime library typically does not rely on synchronization objects with threads. Instead, he saves intermediate results in the data structure of each thread.
The visualc++ has a different version of the C Runtime library in 6. They are selected on the basis of whether the program being compiled is the creation of a debug state or a release version, whether it is a static link to a C run-time library or a dynamic link, and whether the application is single-threaded or multithreaded. Table 17-3 shows the name of the library and the corresponding
The compiler selects the switch.
Table 17-3 C Run-time library versions in VISUAL C + +
Library name |
Application Type |
Selector switch |
Libc.lib |
single thread; static link; release version creation |
/ml |
Libcd.lib |
single thread; static link; create in debug state |
/MLd |
Libcmt.lib |
multi-threaded; static link; release version creation |
/mt |
Licbmtd.lib |
multi-threaded; static link; created under Debug version |
/MTd |
Msvcrt.lib |
Single-threaded or multithreaded; dynamic link; release build creation |
/md |
Msvcrtd.lib |
Single-threaded or multi-threaded; dynamic linking; creating in Debug state |
/MDd |
If you are using Visual C + +, the IDE will add an option switch for you as long as you select the appropriate entry in the Use Run-time library domain in the Project Settings dialog box.
17.1.10 calling MFC member functions across thread bounds
Write bad messages for multithreaded MFC applications. As long as the thread does not invoke the member functions of objects created by other threads, there is little limit to what they do. There will be a variety of bizarre situations. The most basic requirement to avoid is that before calling member functions in MFC objects created by other threads, you must understand their
The implied meaning. Also remember that MFC is not thread-safe. So even if a member function seems to be thread-safe, ask yourself if thread B accesses an object created by thread A, what happens to thread A in the access process, and then threads B?
In practice, multi-threaded MFC applications tend to give a large amount of user interface work to the main thread to complete. If a background thread wants to update the user interface, it sends or publishes the message to the main thread, allowing the main thread to perform the update work.
17.1.11 First Multithreaded Application
Sieve is a dialog-based application that uses the famous Eratosthenes prime algorithm (filtering method). To calculate the number of primes that exist between 2 and all specified numbers. The Motor Start button executes the calculation and ends when the number of results appears in the Window's box.
#definewm_user_thread_finished Wm_user +0x100UINT ThreadFunc (LPVOID pparam);intSieve (intnMax); typedefstructtagthreadparms{intNMax; HWND hwnd;} Threadparms, ..... On_message (wm_user_thread_finished,&cmy17sievedlg::onthreadfinshed)//message... afx_msg LRESULT cmy17sievedlg::onthreadfinshed (WPARAM WPARAM, LPARAM LPARAM)//internal custom message Functions{setdlgitemint (Idc_result, (int) WParam); GetDlgItem (Idc_start)-EnableWindow (TRUE); return 0;}voidCmy17sievedlg::onbnclickedstart ()//Key Events{ intnmax=Getdlgitemint (Idc_max); if(nmax<Ten) {MessageBox ("The number you enter must is ten or higher"); GetDlgItem (Idc_max)-SetFocus (); return; } setdlgitemtext (idc_result,_t ("")); GetDlgItem (Idc_start)-EnableWindow (FALSE); Threadparms*ptp=Newthreadparms; PTP->nmax=NMax; PTP->hwnd=m_hwnd; AfxBeginThread (THREADFUNC,PTP);}//Global FunctionsUINT ThreadFunc (LPVOID pparam)//Thread Global Functions{threadparms*ptp= (threadparms*) Pparam; intNmax=ptp->NMax; HWND hwnd=ptp->hWnd; DeletePTP; intNcount =Sieve (NMax); ::P Ostmessage (hwnd,wm_user_thread_finished, (WPARAM) ncount,0); return 0;}intSieve (intNMAX)//Filter Method Functions{pbyte pbuffer=NewByte[nmax +1]; :: Fillmemory (Pbuffer,nmax+1,1); intNlimit =2; while(Nlimit *nlimit<nMax) Nlimit++; for(intI=2; i<=nlimit;i++) { if(Pbuffer[i]) { for(intk=i+i;k<=nmax;k+=i) pbuffer[k]=0; } } intNcount =0; for(intI=2; i<=nmax;i++) if(Pbuffer[i]) ncount++; Delete[]pbuffer; returnncount;}
View Code
MFC "17-1" thread and thread synchronization