Windows thread Synchronization Detailed

Source: Internet
Author: User
Tags mutex semaphore

Thread Synchronization Issues

In multithreaded programming, errors are extremely easy to produce. The cause of the error: two or more threads concurrently access shared resources (such as global variables, handles, space, etc.), resulting in inconsistencies in the resources being modified by different threads. Multiple threads access to resources in a certain order, but not in the order they were expected to cause unexpected errors in the program.
Problem instance:(Environment: VS2015 console program)

#include<Windows.h>#include<stdio.h>int g_nNum = 0;DWORD WINAPI ThreadProc(LPVOID lParam){for (int i = 0; i < 10000; i++){g_nNum++;}printf("%d", g_nNum);return 0;}int main(){//创建线程1HANDLE hThread1 = CreateThread(NULL, NULL, ThreadProc, NULL, NULL, NULL);//创建线程2HANDLE HThread2 = CreateThread(NULL, NULL, ThreadProc, NULL, NULL, NULL);WaitForSingleObject(hThread1, INFINITE); //线程1执行完毕后返回WaitForSingleObject(HThread2, INFINITE); //线程2执行完毕后返回printf("%d\n", g_nNum);return 0;}

First execution Result:
11789
17876
17876
Second execution Result:
20000
15844
20000
As expected, G_nnum should increment by 10000 per two threads, while in fact the value of g_nnum is indeed indeterminate.
First look at the code for this simple manipulation of the assembler layer:

00AE1419 mov eax,dword ptr ds [00AE8134h]00AE141E add eax,100AE1421 mov dword ptr ds:[00AE8134h],eax

Two threads perform g_nnum++ this operation at the same time, it is possible that thread 1 executes the add eax, does not write the result of the increment, and thread 2 begins execution, and when thread 1 executes, thread 2 executes as if it is useless. Because the scheduling of threads is not controllable, we cannot predict the final result.

Solution: * *

1. Atomic Operation
Atomic operation is a relatively simple operation, only simple addition and subtraction of resources, and so on. When using atomic operations to access data, other threads cannot access this data until the end of the operation, that is, two threads are not allowed to manipulate one data at the same time, and of course, three is not allowed. Atomic operations are like toilets, allowing only one person to enter.
Common atomic manipulation functions Baidu on its own

int g_nNum = 0;DWORD WINAPI ThreadProc(LPVOID lParam){for (int i = 0; i < 10000; i++){//原子操作中的自增,其他的原子操作函数自行百度InterlockedIncrement((unsigned long*)&g_nNum);}printf("%d", g_nNum);return 0;}int main(){HANDLE hThread1 = CreateThread(NULL, NULL, ThreadProc, NULL, NULL, NULL);HANDLE HThread2 = CreateThread(NULL, NULL, ThreadProc, NULL, NULL, NULL);WaitForSingleObject(hThread1, INFINITE);WaitForSingleObject(HThread2, INFINITE);printf("%d\n", g_nNum);return 0;}

Run results
10000
20000
20000

2. Critical section

Atomic operations can only solve thread synchronization problems for individual data (basic operations of integer variables), and most of the time we want to implement the protection of a piece of code, so we introduce the concept of a critical section. The critical section can implement multiple code protected areas through this function pair by entercriticalsection and LeaveCriticalSection. Before using the critical section, call Initiaizecriticalsection to initialize a critical section and call DeleteCriticalSection to destroy the critical section after use.

#include <windows.h>critical_section cs = {};int g_nnum = 0;dword WINAPI threadproc (lpvoid lParam) {//2. Enter the critical section CS has a property LockSemaphore is not locked//When called EnterCriticalSection indicates that the critical section is locked, OwningThread is the thread//other call EnterCriticalSection, will check    and whether the thread at the time of the lock is the same thread//if it is not, the thread that calls enter is blocked//if it is, the lock count lockcount+1//A few times enter has to be several times leave//However, a person who is not the owner thread cannot actively leave    EnterCriticalSection (&AMP;CS);    for (int i = 0; i < 100000; i++) {g_nnum++;    } printf ("%d\n", g_nnum); 3.    Leave the critical section//In case the leave has not been called, the thread crashes, or the loop is dead.    Waiting for someone outside forever waiting//The critical section is not a kernel object and cannot be synchronized across processes LeaveCriticalSection (&AMP;CS); return 0;}    int main () {//1. Initialize Critical zone initializecriticalsection (&AMP;CS);    HANDLE hThread1 = CreateThread (null, NULL, THREADPROC, NULL, NULL, NULL);    HANDLE hThread2 = CreateThread (null, NULL, THREADPROC, NULL, NULL, NULL);    WaitForSingleObject (HThread1, INFINITE);    WaitForSingleObject (HThread2, INFINITE);    printf ("%d\n", g_nnum); 4. Destroy Critical Zone deletecriticAlsection (&AMP;CS); return 0;}
3. Mutex

There are a number of critical areas that cannot be solved because critical sections are valid in one process and cannot be synchronized in multi-process situations. Also, if a thread enters the critical section, and the thread crashes for some reason, i.e. it cannot hold LeaveCriticalSection (), then other threads will no longer be able to enter the critical section, and the program crashes. The mutex can solve these problems.
First, the mutex is a kernel object . (So the mutex has all the properties of the kernel object) it has two States, excited states and non-excited states; it has a concept called thread ownership, similar to a critical section, waiting for the side effect of the mutex, setting the owner of the mutex to this thread, and then setting the state of the mutex to non-excited states.
Main functions: CreateMutex (); WaitForSingleObject (); ReleaseMutex (); Function usage Self-Baidu.
When a thread A calls the WaitForSingleObject function, WaitForSingleObject returns immediately, sets the mutex to a non-excited state, the mutex is locked, and the thread gains ownership. After that, any thread that calls WaitForSingleObject cannot take ownership and must wait for the mutex. When thread A calls ReleaseMutex, the mutex is unlocked, and the mutex is set to the excited state and is randomly selected from the thread that waits for it, repeating the previous procedure. A mutex can only be owned by one thread at a time, and the code between WAITXXXX and ReleaseMutex is protected, similar to the critical section, except that the mutex is a kernel object that can be synchronized in multiple processes.

#include <windows.h>#include<stdio.h>HANDLE hMutex = 0;int g_nNum = 0;// 临界区和互斥体比较// 1. 互斥体是个内核对象,可以跨进程同步,临界区不行// 2. 当他们的拥有者线程都崩溃的时候,互斥体可以被系统释放,变为有信号,其他的等待函数可以正常返回// 临界区不行,如果都是假死(死循环,无响应),他们都会死锁// 3. 临界区不是内核对象,所以访问速度比互斥体快DWORD WINAPI ThreadProc(LPVOID lParam) {    // 等待某个内核对象,有信号就返回,无信号就一直等待    // 返回时把等待的对象变为无信号状态    WaitForSingleObject(hMutex, INFINITE);    for (int i = 0; i < 100000; i++)    {        g_nNum++;    }    printf("%d\n", g_nNum);    // 把互斥体变为有信号状态    ReleaseMutex(hMutex);    return 0;}int main(){    // 1. 创建一个互斥体    hMutex = CreateMutex(        NULL,        FALSE,// 是否创建时就被当先线程拥有        NULL);// 互斥体名称    HANDLE hThread1 = CreateThread(NULL, NULL, ThreadProc, NULL, NULL, NULL);    HANDLE hThread2 = CreateThread(NULL, NULL, ThreadProc, NULL, NULL, NULL);    WaitForSingleObject(hThread1, INFINITE);    WaitForSingleObject(hThread2, INFINITE);    printf("%d\n", g_nNum);    return 0;}
4. Signal Volume

The

Semaphore is similar to the mutex. However, the signal quantity concept is introduced in the semaphore. If the mutex is a home toilet, at a point in time can only be used by one person, the semaphore is a public toilet, can be used at the same time, but there is still a limit. This upper limit is the maximum number of signals.
Main functions: CreateSemaphore (); OpenSemaphore (); ReleaseSemaphore (); WaitForSingleObject (); Function usage Self-Baidu
When the thread calls the WaitForSingleObject (), the current semaphore is reduced by one, and then the thread is called, minus one. is 0 o'clock, when the semaphore is locked, and then the thread calls WaitForSingleObject, it is blocked.

#include <windows.h>#include <stdio.h>HANDLE hSemphore;int g_nNum = 0;DWORD WINAPI ThreadProc(LPVOID lParam) {        WaitForSingleObject(hSemphore, INFINITE);    for (int i = 0; i < 100000; i++)    {        g_nNum++;    }    printf("%d\n", g_nNum);    ReleaseSemaphore(hSemphore,        1,// 释放的信号个数可以大于1,但是释放后的信号个数+之前的不能大于最大值,否则释放失败        NULL);    return 0;}int main(){     hSemphore = CreateSemaphore(        NULL,        1,// 初始信号个数        1,// 最大信号个数,就是允许同时访问保护资源的线程数        NULL);    HANDLE hThread1 = CreateThread(NULL, NULL, ThreadProc, NULL, NULL, NULL);    HANDLE hThread2 = CreateThread(NULL, NULL, ThreadProc, NULL, NULL, NULL);    WaitForSingleObject(hThread1, INFINITE);    WaitForSingleObject(hThread2, INFINITE);    printf("%d\n", g_nNum);    return 0;}
5. Events

The

event has large permissions. You can manually set whether the event object is an excited state or a non-excited state. When you create a time object, you can set it to be auto-selected and manually selected. Automatically selects events that, when the function returns, automatically sets their state to non-excited states, blocking other threads. Manually selected, the state of the event object is controlled by the code.
Main functions: CREATEEVENTW (); Openeventa (); SetEvent (); PulseEvent ();
Closeevent (); roseevent ();

  #include <windows.h> #include <stdio.h>handle hEvent1, Hevent2;dword WINAPI Threadproca (LPVOID        LParam) {for (int i = 0; i < ten; i++) {WaitForSingleObject (hEvent1, INFINITE);        printf ("A");    SetEvent (HEvent2); } return 0;} DWORD WINAPI THREADPROCB (LPVOID lParam) {for (int i = 0; i <; i++) {WaitForSingleObject (HEvent2, INFINITE        );        printf ("B");    SetEvent (HEVENT1); } return 0;} int main () {///event object, height-Customized hEvent1 = CreateEvent (NULL, false,//Auto reset true,//has signal NULL)    ; HEvent1 automatically resets the initial signal to any person via setevent to a signal resetevent to no signal//HEVENT2 automatically resets the initial no signal HEvent2 = CreateEvent (NULL, FALSE,    FALSE, NULL);    HANDLE hThread1 = CreateThread (null, NULL, THREADPROCA, NULL, NULL, NULL);    HANDLE hThread2 = CreateThread (null, NULL, THREADPROCB, NULL, NULL, NULL);    WaitForSingleObject (HThread1, INFINITE);    WaitForSingleObject (HThread2, INFINITE); return 0;}  

Detailed Windows thread synchronization

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.