Multithreading undoubtedly brings a lot of convenience, improve a lot of development efficiency, but also brought a lot of problems.
Give me a chestnut:
DWORD WINAPI ThreadProc (LPVOID lpparameter);intm =0;intn =0;intMainintARGC, tchar* argv[], tchar*envp[]) {HANDLE Hthread= CreateThread (NULL,0, (Lpthread_start_routine) Threadproc,null,0, NULL); inti =0; for(i=0;i<100000; i++) {m++; N++; } Sleep (1); cout<<"M:"<<m<<Endl; cout<<"N:"<<n<<Endl; return 0;} DWORD WINAPI ThreadProc (lpvoid lpparameter) {intj =0; for(j=0;j<100000; j + +) {m++; N++; }
return 0;}
What is the output of this program?
According to common sense, it is not difficult to conclude that M and N are all 20000
But what about the actual results?
It was a surprise, and the results were different every time ! Why does this happen?
A thread does not control access to global resources when accessing global resources, and when a takes out memory data into the register operation, B also takes out the memory data and puts it into the register operation, it is possible to cause repeated operations! In other words, a is in operation, the result of the operation has not been put back, B is also the operation of the line. So the final result is smaller than 200000 and M is not the same as N.
In order to control the thread do not disorder to be in accordance with our original intention of the coordination, the inevitable need to use the line program control (mutual exclusion/synchronization) technology!
Here 's a basic approach to threading control :
1.EVENT (Event)
2.Critical section (critical area)
3.Mutex (Mutex)
4.Semaphore (semaphore)
0x01
Event (events)
Synchronizing threads with events (event) is the most resilient . There are two states of an event: the firing state and the non-excited state. also known as signal status and no signal status . There are two types of events: Manual reset events and automatic reset events. when a manual reset event is set to the firing state, all waiting threads are awakened, and remain in the firing state until the program resets it to the non-fired state. When the auto-reset event is set to the firing state, the "one" waiting thread is awakened, and then automatically reverts to the non-fired state. Therefore, it is ideal to synchronize two threads with an auto-reset event . The corresponding class in MFC is CEvent. The CEvent constructor creates an auto-reset event by default and is not in the fired state. Change the state of an event: Setevent,resetevent.
Let's look at how to use events to solve our practical problems:
DWORD WINAPI ThreadProc (LPVOID lpparameter);intm =0;intn =0;intMainintARGC, tchar* argv[], tchar*envp[]) {HANDLE hevent=NULL; Hevent= CreateEvent (null,false,false,null);//Falsexô¶¯trueêö¶¯falseêçxô¶¯HANDLE hthread= CreateThread (NULL,0, (Lpthread_start_routine) threadproc,hevent,0, NULL); WaitForSingleObject (Hevent,infinite); inti =0; for(i=0;i<100000; i++) {m++; N++; } Sleep (1); cout<<"M:"<<m<<Endl; cout<<"N:"<<n<<Endl; return 0;} DWORD WINAPI ThreadProc (lpvoid lpparameter) {HANDLE hevent=NULL; Hevent=(HANDLE) Lpparameter; intj =0; for(j=0;j<100000; j + +) {m++; N++; } SetEvent (hevent); return 0;}
Let's see if it turns out.
The results are correct!
0X02
Critical section (critical area)
Critical_section is the fastest. Other kernel locks (events, mutexes) require thousands of CPU cycles each time the kernel is entered.
The first advice to use a critical area is not to lock a resource for long periods of time. The long time here is relative, depending on the program. For some control software, it may be a few milliseconds, but for some other programs it can take up to a few minutes. However, after entering the critical section, the resources must be released as soon as possible. What happens if I don't release it? The answer is not how. If it is the main thread (GUI thread) to enter a non-released critical section, hehe, the program will be hung! A disadvantage of a critical region is that the Critical section isnot a core object , and it is not known that the thread entering the critical zone is alive or dead, and if the thread entering the critical section is hung, the critical resource is not released and the system cannot be informed. And there is no way to release the critical resource. This drawback is remedied in the mutex (mutex).
DWORD WINAPI ThreadProc (LPVOID lpparameter);intm =0;intn =0; Critical_section CS; intMainintARGC, tchar* argv[], tchar*envp[]) {InitializeCriticalSection (&CS); HANDLE Hthread= CreateThread (NULL,0, (Lpthread_start_routine) Threadproc,null,0, NULL); inti =0; EnterCriticalSection (&CS); for(i=0;i<100000; i++) {m++; N++; } leavecriticalsection (&CS); Sleep (1); cout<<"M:"<<m<<Endl; cout<<"N:"<<n<<Endl; DeleteCriticalSection (&CS);//dead Lock return 0;} DWORD WINAPI ThreadProc (lpvoid lpparameter) {intj =0; EnterCriticalSection (&CS); for(j=0;j<100000; j + +) {m++; N++; } leavecriticalsection (&CS); return 0;}
The same way you can get the right results!
The use of a critical section is a four-step problem: Initializing the critical area----entering the critical area----leaving the critical area--and destroying the critical area . Keep in mind that there will be no problem with this four. One thing to note is that the critical section and recursion should be used with caution, and recursion in the critical section will cause deadlocks !
0X03
Mutex (mutex)
The function of the mutex is similar to the critical area. The difference is that the mutex takes more time than the critical section, but the mutex is the core object (Event, Semaphore also), can be used across processes, and waits for a locked mutex to set a TIMEOUT, It will not be as critical section does not know the situation of the critical area, and has been death. The WIN32 function has: Create a mutex CreateMutex (), open the Mutex OpenMutex (), release the Mutex ReleaseMutex (). The ownership of a mutex does not belong to the thread that generated it, but to the last thread that waits for the mutex (WaitForSingleObject, and so on) and has not yet performed the ReleaseMutex () operation. Threads have mutexes as if they had entered the critical section, only one thread at a time can have the mutex. If a thread that owns a mutex does not call ReleaseMutex () before returning, the mutex is discarded, but when the other thread waits for the mutex (WaitForSingleObject, etc.), it can still return and get a wait_ Abandoned_0 the return value. Being able to know that a mutex is discarded is unique to the mutex.
HANDLE Hmutex =NULL; intMainintARGC, tchar* argv[], tchar*envp[]) {Hmutex=CreateMutex (null,true,null); HANDLE Hthread= CreateThread (NULL,0, (Lpthread_start_routine) Threadproc,null,0, NULL); inti =0; for(i=0;i<100000; i++) {m++; N++; } ReleaseMutex (Hmutex); Sleep (1); cout<<"M:"<<m<<Endl; cout<<"N:"<<n<<Endl; return 0;} DWORD WINAPI ThreadProc (lpvoid lpparameter) {WaitForSingleObject (hmutex,infinite); intj =0; for(j=0;j<100000; j + +) {m++; N++; } return 0;}
0x04
Semaphore (semaphore)
The semaphore is the most historical synchronization mechanism. Semaphore is a key factor to solve the producer/consumer problem. The Win32 function CreateSemaphore () is used to generate the semaphore. ReleaseSemaphore () is used to unlock the lock. The present value of semaphore represents the number of resources currently available, and if the present value of semaphore is 1, there is a lock action that can be successful. If the present value is 5, then there are five locking actions that can be successful. When you call wait ... Such functions require locking, if the semaphore present value is not 0,wait ... Return immediately, the number of resources minus 1. When the number of ReleaseSemaphore () resources is called plus 1, then the total number of resources initially set is not exceeded.
HANDLE Hsemaphore =NULL;intMainintARGC, tchar* argv[], tchar*envp[]) {Hsemaphore= CreateSemaphore (NULL,0,1, NULL); HANDLE Hthread= CreateThread (NULL,0, (Lpthread_start_routine) Threadproc,null,0, NULL); inti =0; for(i=0;i<100000; i++) {m++; N++; } releasesemaphore (Hsemaphore,1, NULL); Sleep (1); cout<<"M:"<<m<<Endl; cout<<"N:"<<n<<Endl; CloseHandle (Hsemaphore); return 0;} DWORD WINAPI ThreadProc (lpvoid lpparameter) {WaitForSingleObject (hsemaphore,infinite); intj =0; for(j=0;j<100000; j + +) {m++; N++; } return 0;}
Windows line-controlled system