Classification of Threads 1. There is a message loop thread
- MFC has a user interface thread that derives a new class from CWinThread as the UI thread class Cuithread, and then calls AfxBeginThread (Runtime_class (Cuithread)) and starts the thread. The UI thread can create modal dialogs directly without worrying about message loops because the UI thread defaults to a message loop.
- MFC non-user interface threads, you cannot create modal dialogs, but you can create non-modal dialog boxes or normal Windows, but you must write your own message loops.
msg msg; while 0 0 ) { translatemessage (&msg); DispatchMessage (&msg); }
2. No message loop thread
- Worker threads in MFC
- Other normal threads that do not have a message loop.
Communication between Threads 1. Shared Memory variables
Because threads are shared process memory, the most efficient communication is through global/static variables. Parameters need to consider whether to add volitile.
L pass parameters, such as references and pointers. Parameters need to consider whether to add volitile.
2. Message notification
- If a child thread communicates to the main thread because the main thread has a message loop, the child thread can communicate to the main thread by sending a message. The coupling of global variables can be avoided by using message communication.
The SendMessage must wait for the message function processing to complete before returning, and PostMessage directly into the message queue to return immediately. So the message parameter of SendMessage can be a temporary variable, and the message parameter of PostMessage must guarantee enough lifetime.
- If a child line threads a custom message loop, you can also specify thread communication through PostThreadMessage.
while (true) { if(GetMessage (&msg,0,0,0//get Msgfrom message Queue { switch(msg.message) { case My_msg:// Todo:break ; } } ;
3. Other ways
- All cross-process communication methods, of course, can be used across threads.
State 1 between threads. Asynchronous
That is, multiple threads are independent of each other and are unaffected by external threads. The thread itself is a way to implement Asynchrony.
2. Synchronization
That is, multiple threads depend on each other, and thread a evaluates to the premise of thread B's calculation, which means that you must wait for thread A to calculate before starting the calculation of thread B.
3. Mutex
That is, when multiple threads operate on the same resource, one thread must wait for the other thread to finish before proceeding. Mutual exclusion differs from synchronization in that mutual exclusion does not have a succession relationship. The same resource can refer to a global variable or to a file object or other kernel object. Because kernel objects are cross-process, they are more cross-threading.
Wait for function 1. Concept
The WaitForSingleObject function is to wait for the kernel object to return from no signal state to signaled state or timeout. That is, there is no signal state when waiting, there is a signal or timeout to return immediately.
The Waitformulitpleobjects function is to wait for multiple kernel objects to return from a signal-free state to a signaled state or a timeout (which can indicate all objects or any object).
Windows has several kernel objects that can be in the notification and non-notification states: Processes, threads, jobs, files, console input/output/error streams, events, wait timers, semaphores, mutex objects.
2. Wait for the relationship between the function and the kernel object
Object |
No signal status |
There is a signal status |
Successful waiting for side effects |
Process |
When process activity |
When the process terminates |
No |
Thread |
When thread activity |
When a thread terminates |
No |
File |
When I/O requests are being processed |
At the end of the I/O request |
No |
Console input |
There is no input |
When input is present |
No |
File modification Notification |
No file modification notifications |
When file system Discovery is modified |
Reset Notifications |
Automatic Reset Events |
ResetEvent, PulseEvent or waiting for success |
When you call SetEvent or pulseevnet |
Resetting events |
Manual reset Event |
ResetEvent, or PulseEvent |
When you call SetEvent or pulseevnet |
No |
Auto Reset Timer |
Cancelwaitabletimer or waiting for success |
When time is up (SetWaitableTimer) |
Reset Timer |
Manual Reset Timer |
Cancelwaitabletimer |
When time is up (SetWaitableTimer) |
No |
Signal Volume |
Waiting for success |
When the number of resources is >0 (ReleaseSemaphore) |
Quantity minus 1 |
Mutex Amount |
Waiting for success |
When not owned by a thread (ReleaseMutex) |
Get thread ownership |
The threads and processes are created and run without a signal state and become signaled when the end runs.
L automatically resets the event (FALSE) object, which is changed to a signal-free state when it waits for success.
The L semaphore object, when called ReleaseSemaphore (quantity plus 1), is in a signaled state, WaitForSingleObject is triggered and the number of signals is immediately reduced by 1.
Advantages and disadvantages of user mode and kernel mode 1. User mode
Advantage: Thread synchronization mechanism is fast
Cons: Easy to get into a deadlock state a problem occurs with thread synchronization between multiple processes. (such as competing resources, deadlocks)
2. Kernel mode
Pros: Support for thread synchronization between multiple processes to prevent deadlocks
Disadvantage: The thread synchronization mechanism is slow and the thread must switch from user mode to kernel mode. This conversion takes a lot of cost: a round trip needs to occupy about 1 0 0 0 C P u cycles on the X 8 6 platform.
State processing between Threads 1. The asynchronous thread
Because the thread itself is asynchronous.
2. Synchronization of Threads
Thread synchronization is primarily through the event kernel object, semaphore (Semaphore) kernel object, and mutex (mutex) kernel objects. Because they are all kernel objects, you can not only operate across threads, but also synchronize across processes.
1. Synchronization of Threads
Thread synchronization is primarily through the event kernel object, semaphore (Semaphore) kernel object, and mutex (mutex) kernel objects. Because they are all kernel objects, you can not only operate across threads, but also synchronize across processes.
Events (Event) Kernel Objects
There are two types of events: Manual reset events and auto-reset events, which need to be manually called ResetEvent after triggering WaitForSingleObject to set the event to no signal , and the latter automatically sets the event to no signal state after triggering waitforsingleobject.
Common functions:
CreateEvent, create the event object.
OpenEvent, opens an event object that has already been created and can be opened across processes.
SetEvent, set the event object to a signaled state.
ResetEvent, set the event object to no signal state.
PulseEvent, the event object is set to a signaled state and then set to a no-signal state, which is not commonly used.
handelg_hevent;intMain () {g_hevent=CreateEvent (null, TRUE, FALSE, null); _beginthreadex (NULL,0, THREADFUN1,0); _beginthreadex (NULL,0, ThreadFun2,0); Setevnet (g_hevent);//}dword WINAPIThreadFun1 (PVOID pparam) {WaitForSingleObject (g_hevent);//Todo ...SetEvent (g_hevnet);return 0;} DWORD WINAPIThreadFun2 (PVOID pparam) {WaitForSingleObject (g_hevent);//Todo ...SetEvent (g_hevnet);return 0;}
Note: If you create a manual reset event above, the two thread functions will execute. If the event is automatically reset, only one thread can be executed, and there is no guarantee which thread will execute first. If you want to ensure that a thread executes first, you can add an event object to ensure that the specified thread has been executed and that the thread has been executed without the order of the Code.
2. Semaphore (Semaphore) Kernel object
Usage rules for semaphores:
The current semaphore resource number is greater than 0 and is marked as signaled.
The current semaphore resource number is 0 and is marked as no signal state.
The semaphore resource number cannot be negative, and the maximum number cannot exceed the specified quantity.
Common functions:
CreateSemaphore, creates a semaphore object.
OpenSemaphore, opens the specified semaphore object, which can be cross-process.
Releasesemaphoer, resource calculation plus 1.
handelg_hsema[2];intMain () {g_hsema[0] =createsemaphore (NULL,1,1, NULL); g_hsema[1] =createsemaphore (NULL,0,1, NULL); _beginthreadex (NULL,0, THREADFUN1,0); _beginthreadex (NULL,0, ThreadFun2,0);} DWORD WINAPIThreadFun1 (PVOID pparam) {WaitForSingleObject (g_hsema[0]);//Todo ...Releasesemaphoer (g_hsema[1]);return 0;} DWORD WINAPIThreadFun2 (PVOID pparam) {WaitForSingleObject (g_hsema[1]);//Todo ...Releasesemaphoer (g_hsema[0]);return 0;}
This ensures that the THREADFUN1 executes, executes the ThreadFun2, and then executes the THREADFUN1, and guarantees that each thread function can only be called once.
3. Mutex (mutex) kernel object
The mutex kernel object ensures that the thread has exclusive access to a single resource. In behavioral characteristics, the mutex is the same as the critical section. However, the mutex is a kernel object, and it is time consuming to switch from user mode to kernel mode. But because it is a kernel object, the mutex is able to cross the process and can set the timeout time, which is more flexible than the critical section.
Common functions:
CreateMutex, creates a mutex object.
OpenMutex, which opens a specified mutex object, can be cross-process.
ReleaseMutex, releasing the mutex, the object is marked as signaled, and the WaitForSingleObject is triggered.
A mutex is like a critical section, with the notion that the current mutex and the current critical section are freed only by the current thread, and that other threads are not freed. Because the mutex is a kernel object, if the thread has terminated, but the mutex it belongs to is still not released, the kernel manager is automatically freed. The critical section does not have this function because the critical section is not a kernel object, so a critical section can cause a deadlock if it is not properly disposed.
Handlecreatemutex (Lpsecurity_attributeslpmutexattributes,
BOOL Binitialowner, LPCTSTR lpname);
Whether the Binitialowner tag owns thread ownership by the creation thread, true indicates that the creator owns, false means that the creator does not own, then the first thread that calls WaitForSingleObject gets thread ownership.
Handelg_hmutex;intMain () {G_hmutex=CreateMutex (Null,false); _beginthreadex (NULL,0, THREADFUN1,0); _beginthreadex (NULL,0, ThreadFun2,0);} DWORD WINAPIThreadFun1 (PVOID pparam) {WaitForSingleObject (G_hmutex);//Todo ...ReleaseMutex (G_hmutex);return 0;} DWORD WINAPIThreadFun2 (PVOID pparam) {WaitForSingleObject (G_hmutex);//Todo ...ReleaseMutex (G_hmutex);return 0;}
Two functions who first call, who gets thread ownership. If you want to specify that the thread run first, you need to determine that the specified thread has already executed and then create a new thread, and you cannot rely on the thread's code creation sequence.
3. Mutual exclusion of threads
Like mutex objects can also achieve mutually exclusive effect, but the mutex is richer, and if it is a simple resource mutex, the use of the critical section is more efficient.
A critical section (Critical) is a piece of code that is accessed exclusively by threads, which means that if a thread is accessing the snippet, the other thread wants to access it, only to wait for the current thread to leave the snippet before it can enter, which ensures thread safety. He works at the user level (relative to the kernel level) and critical_section the critical area correlation mechanism in the window System.
Common functions:
Voidinitializecriticalsection (lpcritical_section lpcriticalsection)//Initialize critical section
Voidentercriticalsection (lpcritical_section lpcriticalsection)//Enter the critical section
Voidleavecriticalsection (lpcritical_section lpcriticalsection)//Exit critical section
Voiddeletecriticalsection (lpcritical_section lpcriticalsection)//release Critical Zone resources
Because the critical section has the concept of thread ownership, the line friend into the critical section has the right to release the critical section. Because the current thread must enter and release, more often, the critical section is used in a function, in order to ensure that there is no release due to the intermediate exit function, we can ensure the release in the following way.
classMutex { Public: Mutex () {initializecriticalsection (section);} ~Mutex () {deletecriticalsection (section);} voidEnter () {entercriticalsection (section);} voidLeave () {leavecriticalsection (section);} structLock;protected: Mutex (Constmutex&); Mutex&operator=(Constmutex&); critical_section section;}; Structmutex::lock {Mutex&s; Lock (Mutex&s): s (s) {s.enter ();} ~Lock () {S.leave ();}}; DWORD Winapithreadfun (PVOID pparam) {mutex::locklock (Mutex);//Todo ... return 0;}
Attention
1. Note that all kernel objects need to call CloseHandle () at the end.
2. Calling MFC object functions across threads is not secure. Because some functions of an MFC object are associated with TLS, some calls can be faulted. such as UpdateData (), it is best to use the handle to send messages to complete the corresponding function.
Multithreaded programming under Windows (II)