《Windows核心编程》第八章——用户模式下的线程同步

下面起了两个线程,每个对一个全局变量加500次,不假思索进行回答,会认为最后这个全局变量的值会是1000,然而事实并不是这样:

#include<iostream>
#include <process.h>
#include <windows.h>

using namespace std;
typedef unsigned int (_stdcall *PThreadFunc)(void*);
int g_nCount = 0;

unsigned int _stdcall ThreadTest1(void*)
{
    for (int i = 0; i < 500; i++)
    {
        g_nCount++;
    }
    
    return 0;
}

unsigned int _stdcall ThreadTest2(void*)
{
    for (int i = 0; i < 500; i++)
    {

        g_nCount++;
    }
    return 0;
}

void main()
{
   g_nCount = 0;
   HANDLE h1 = (HANDLE)_beginthreadex(NULL, 0, ThreadTest1, NULL, 0, NULL);
   HANDLE h2 = (HANDLE)_beginthreadex(NULL, 0, ThreadTest2, NULL, 0, NULL);
   HANDLE hs[2] = {h1, h2};
   WaitForMultipleObjects(2, hs, TRUE, INFINITE);
   CloseHandle(h1);
   CloseHandle(h2);
  printf("Global count:%d
", g_nCount); getchar(); 
}

然而运行多次、每次结果都不同,而且,几乎不会等于1000:

造成这种现象的原因很简单,就是g_nCount在进行自增的时候没有实现原子操作,g_nCount的本质其实是:

  • Interlocked函数

为了保证自增的原子性,改为使用Interlocked函数:

#include<iostream>
#include <process.h>
#include <windows.h>

using namespace std;
typedef unsigned int (_stdcall *PThreadFunc)(void*);
int g_nCount = 0;

unsigned int _stdcall ThreadTest1(void*)
{
    for (int i = 0; i < 500; i++)
    {
        //Sleep(12);
        //g_nCount ++;
        InterlockedIncrement((volatile unsigned long long*)&g_nCount);
    }
    
    return 0;
}

unsigned int _stdcall ThreadTest2(void*)
{
    for (int i = 0; i < 500; i++)
    {
        //Sleep(10);
        //g_nCount ++;
        InterlockedIncrement((volatile unsigned long long*)&g_nCount);
    }
    return 0;
}

void main()
{
    g_nCount = 0;
    HANDLE h1 = (HANDLE)_beginthreadex(NULL, 0, ThreadTest1, NULL, 0, NULL);
    HANDLE h2 = (HANDLE)_beginthreadex(NULL, 0, ThreadTest2, NULL, 0, NULL);
    HANDLE hs[2] = { h1, h2 };
    WaitForMultipleObjects(2, hs, TRUE, INFINITE);
    CloseHandle(h1);
    CloseHandle(h2);
    
    printf("Global count:%d
", g_nCount);
    getchar();
}

这样就保证了自增的原子性。

  •  条件变量的使用
#include <iostream>
#include <windows.h>
#include <vector>
#include <process.h>
#include "Queue.h"

using namespace std;
CQueue g_Queue;
SRWLOCK g_srwLock;
CONDITION_VARIABLE g_cvProduce;
CONDITION_VARIABLE g_cvConsume;

int g_nCount = 0;
int g_nWriterCount = 0;
int g_nReaderCount = 0;

unsigned int _stdcall WriterThread(void* pParam)
{
    g_nWriterCount++;
    //printf("Enter writerthread-%d
", g_nWriterCount);
    while (TRUE)
    {
        Sleep(1000);
        AcquireSRWLockExclusive(&g_srwLock);
        if (g_Queue.IsFull())
        {
            printf("Queue is full..
");
            SleepConditionVariableSRW(&g_cvProduce, &g_srwLock, INFINITE, 0);
        }
        /*else
        {
            
        }*/
        g_Queue.AddElement(g_nCount);
        printf("Produce element:%d
", g_nCount);
        g_nCount++;
        ReleaseSRWLockExclusive(&g_srwLock);
        WakeConditionVariable(&g_cvConsume);
    }
    return 0;
}

unsigned int _stdcall ReaderThread(void* pParam)
{
    g_nReaderCount++;
    //printf("Enter readerthread-%d
", g_nReaderCount);
    while (TRUE)
    {
        Sleep(2000);
        //这里使用的例子和书中的例子有所不同,书中的例子中的ReaderThread仅仅是读取队列中的内容,而这里

     //会去修改队列的内容,所以不能使用AcquireSRWLockShared. AcquireSRWLockExclusive(&g_srwLock); if (g_Queue.IsEmpty()) { printf("Queue is empty.. "); SleepConditionVariableSRW(&g_cvConsume, &g_srwLock, INFINITE, 0); } /*else { }*/ printf("Consume element:%d ", g_Queue.DelElement()); ReleaseSRWLockExclusive(&g_srwLock); WakeAllConditionVariable(&g_cvProduce);//don't use wakeconditionvariable(). } return 0; } void main() { InitializeSRWLock(&g_srwLock); HANDLE hWriter1 = (HANDLE)_beginthreadex(NULL, 0, WriterThread, NULL, 0, NULL); HANDLE hWriter2 = (HANDLE)_beginthreadex(NULL, 0, WriterThread, NULL, 0, NULL); HANDLE hReader1 = (HANDLE)_beginthreadex(NULL, 0, ReaderThread, NULL, 0, NULL); HANDLE hReader2 = (HANDLE)_beginthreadex(NULL, 0, ReaderThread, NULL, 0, NULL); HANDLE hReader3 = (HANDLE)_beginthreadex(NULL, 0, ReaderThread, NULL, 0, NULL); HANDLE hArray[5] = { hWriter1, hWriter2, hReader1, hReader2, hReader3 }; WaitForMultipleObjects(5, hArray, TRUE, INFINITE); CloseHandle(hWriter1); CloseHandle(hWriter2); CloseHandle(hReader1); CloseHandle(hReader2); CloseHandle(hReader3); getchar(); }

原文地址:https://www.cnblogs.com/predator-wang/p/8905815.html