Data transmission is often required when multithreading is used. For example, when a sub-thread finishes execution, it must tell the main thread that the process has ended, as shown in the following figure, multiple Threads may modify the global data at the same time. Although we mentioned above, we try to use local variables and function parameters in the thread as much as possible, however, thread synchronization is required when global variables are inevitably used and their values are modified.
Communication between threads is very important, especially in the following two cases:
1: multiple threads need to access a shared resource at the same time, and the resource integrity cannot be damaged.
2: one thread needs to notify other threads that a task has been completed.
WINDOWS provides many thread synchronization mechanisms. The following describes thread synchronization in several user modes.
1: Atomic access
Interlocked functions are mainly used. Interlocked functions can only perform atomic access to a single variable, the system will wait for other threads that access shared resources.
Common Interlocked functions include:
LONG InterlockedExchangeAdd( PLONG volatile plAddend, LONG lIncrement);LONGLONG InterlockedExchangeAdd64( PLONGLONG volatile pllAddend, LONGLONG llIncrement);
This function distinguishes different CPU platforms and adds the llIncrement atomic operation to the memory data pointed to by the plAddend pointer. Of course, if this llIncrement is negative, the operation is reduced.
This function can be completely replaced
InterlockedIncrement
This function sets llIncrement to the default value of 1.
The following are some atomic operations for exchange.
LONG InterlockedExchange( PLONG volatile plTarget, LONG lValue);LONGLONG InterlockedExchange64( PLONGLONG volatile plTarget, LONGLONG lValue);PVOID InterlockedPointer( PVOID *volatile ppvTarget, PVOID pvValue);
Note that the third function is a pointer to be exchanged, so here ppvTarget is a second-level pointer.
This set of function types have returned values. The returned values are the data stored in the original ppvTarget. This ensures that the data in ppvTarget is reset without losing the original data, and plays a good role in realizing the rotation lock.
Bool use=false;Void func(){ While(InterlockedExchange(&use,true)==true) Sleep(0);//........InterlockedExchange(&use,false);}
The following is the code for implementing the rotation lock. Here we compare the use, that is, whether shared resources are accessed. First we set this value to true, and then compare whether the original value is true, that is, whether the resource is occupied. If the resource is occupied, the current thread will discard the scheduling of the time slice. If the resource is not occupied, the resource can be used, finally, after using the shared resources, you need to re-set the shared resources access flag to flase to indicate that the resources are no longer occupied.
It should be noted that it is meaningless to use the loop lock on a single CPU system. It is only useful on multiple CPU Systems. When a resource is used by a thread on a single CPU, other threads can wait on other CPUs. multi-threaded reading and writing of multiple CPUs is slower than that of a single CPU, because data on the cache must be synchronized between multiple CPUs, the volatile keyword indicates that the variable may be modified by something other than the application, telling the compiler not to optimize the variable.
Interlocked functions are fast to use, but can only process a single data. There are a lot of data to be synchronized. Here we will introduce the second user mode synchronization mechanism.
2. Use the reference section
The key section is a short piece of code. Before execution, it needs to exclusively access some shared resources. In core programming, this shared resource is equivalent to the restroom in the restroom, a restroom allows only one person to access the restroom at the same time. When this person enters the restroom, he must change the restroom usage status to in use, so that other people cannot access the restroom at this time, when this person leaves the toilet, he needs to change the toilet usage status to unattended to facilitate other people's use. This metaphor is very vivid. This person is a thread. The initialization operation is required before the restroom is used. If there is no analogy in this article, we can just look at it before the restroom is used, you need to take off your pants, that is, initialize the job.
VOID InitializeCriticalSection(PCRITICAL_SECTION pcs);
After the restroom is installed, we can go to the bathroom.
Int g_a=0;CRITICAL_SECTION cs;DWORD WINAPI ThreadProc1(PVOID){EnterCriticalSection(&g_a);for(int i=0;i<100;i++) g_a++;LeaveCriticalSection(&cs); return 0;}DWORD WINAPI ThreadProc2(PVOID){EnterCriticalSection(&g_a); for(int i=0;i<100;i++) g_a++;LeaveCriticalSection(&cs);return 0;}
The shared resource g_a here is our restroom. Of course, the restroom must have been initialized before, but the resources can be exclusively used through Enter and Leave. When we leave the restroom, we need to clean it up. Here we can put on our pants.
This method is not very good, because when we find that the toilet is occupied, we are always waiting there, here we think we should find a secret or servant to help us see if we can go to the bathroom now. Here we use the TryEnterCriticalSection function, which is our servant, this function returns true or false, indicating whether the restroom in the restroom is occupied. This servant will help us stay in the restroom, while our main thread) can do something else.
3. read/write lock
The difference between read and write operations is that read operations can be performed at the same time, but write operations cannot be performed. Here, the Read and Write locks are used to differentiate the differences.
Similar to the key segment, Initialization is also required before the read/write lock is used. Here we can compare the resources required for the read operation to the taps in the toilet and the write operation to the restroom, at the same time, we can wash our hands at the same time, but not at the same time. This should be in line with common sense.
First, initialize the system. Take off your pants and take out your hands when you wash your hands.
Void InitializeSRWLock(PSRWLOCK SRWLock);
What should I do later?
Let's talk about the restroom first.
Void AcquireSRWLockExclusive(PSRWLOCK SRWLock);
Or, when it's used up, remember to pick up your pants and perform other cleanup operations.
Void ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
Hand washing
Void AcquireSRWLockShared(PSRWLOCK SRWLock);Void ReleaseSRWLockShared(PSRWLOCK SRWLock);
The form is surprisingly similar, that is, the last one is resources that can be shared, and the other is resources that must be exclusive.
When other people wait for the restroom, we generally don't want to wait there. We want others to tell us whether there are people in the restroom, whether the faucet or toilet can be used, this is because we want to do other things at this time. Here we talk about the rest. Windows provides the SleepConditionVariableCS or SleepConditionVariableSRW function, waiting for the conditional variable. When a thread is waiting for the condition variable, it releases the lock in an atomic manner and blocks itself until the condition variable is triggered.
Bool SleepConditionVariableCS( PCONDITION_VARIABLE pConditionVariable, PCRITICAL_SECTION pCriticalSection, DWORD dwMilliseconds);Bool SleepConditionVariableSRW( PCONDITION_VARIABLE pConditionVariable, PSRWLOCK pSRWLock, DWORD dwMilliseconds ULONG Flags);
When another thread detects that the corresponding conditions have been met, for example, an element can be read by the reader thread. It calls WakeConditionVariable or WakeAllConditionVariable to trigger the conditional variable. In this way, the thread that calls the Sleep function and blocks the conditional variable will be awakened.
Void WakeConditonVariable( PCONDITION_VARIABLE ConditionVariable);Void WakeAllConditionVariable( PCONDITION_VARIABLE ConditionVariable);
DwMilliseconds indicates that we want to wait for the time. We don't need to wait until this time even if no one uses the restroom,
Flag indicates the way in which we will obtain the lock. Here there is the CONDITION_VARIABLE_LOCKMODE_SHARED sharing method. Here 0 is passed, indicating the exclusive method.
This article from the "selling cute programmers" blog, please be sure to keep this source http://7677869.blog.51cto.com/7667869/1302886