zoukankan      html  css  js  c++  java
  • 《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(); }

  • 相关阅读:
    训练20191009 2018-2019 ACM-ICPC, Asia East Continent Finals
    [学习笔记] 有上下界网络流
    [HDU4609] 3-idiots
    [HDU1402] A * B Problem Plus
    [HNOI2017] 礼物
    训练20191007 2017-2018 ACM-ICPC Latin American Regional Programming Contest
    [ZJOI2014] 力
    训练20191005 2017-2018 ACM-ICPC Asia East Continent League Final
    [一本通学习笔记] 树链剖分
    [一本通学习笔记] 快速幂
  • 原文地址:https://www.cnblogs.com/predator-wang/p/8905815.html
Copyright © 2011-2022 走看看