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

#include<iostream>
#include <process.h>
#include <windows.h> using namespace std;
typedef unsigned int (_stdcall *PThreadFunc)(void*);
int g_nCount = ; unsigned int _stdcall ThreadTest1(void*)
{
for (int i = ; i < ; i++)
{
g_nCount++;
} return ;
} unsigned int _stdcall ThreadTest2(void*)
{
for (int i = ; i < ; i++)
{ g_nCount++;
}
return ;
} 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\n", 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 = ; unsigned int _stdcall ThreadTest1(void*)
{
for (int i = ; i < ; i++)
{
//Sleep(12);
//g_nCount ++;
InterlockedIncrement((volatile unsigned long long*)&g_nCount);
} return ;
} unsigned int _stdcall ThreadTest2(void*)
{
for (int i = ; i < ; i++)
{
//Sleep(10);
//g_nCount ++;
InterlockedIncrement((volatile unsigned long long*)&g_nCount);
}
return ;
} void main()
{
g_nCount = ;
HANDLE h1 = (HANDLE)_beginthreadex(NULL, , ThreadTest1, NULL, , NULL);
HANDLE h2 = (HANDLE)_beginthreadex(NULL, , ThreadTest2, NULL, , NULL);
HANDLE hs[] = { h1, h2 };
WaitForMultipleObjects(, hs, TRUE, INFINITE);
CloseHandle(h1);
CloseHandle(h2); printf("Global count:%d\n", 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 = ;
int g_nWriterCount = ;
int g_nReaderCount = ; unsigned int _stdcall WriterThread(void* pParam)
{
g_nWriterCount++;
//printf("Enter writerthread-%d\n", g_nWriterCount);
while (TRUE)
{
Sleep();
AcquireSRWLockExclusive(&g_srwLock);
if (g_Queue.IsFull())
{
printf("Queue is full..\n");
SleepConditionVariableSRW(&g_cvProduce, &g_srwLock, INFINITE, );
}
/*else
{ }*/
g_Queue.AddElement(g_nCount);
printf("Produce element:%d\n", g_nCount);
g_nCount++;
ReleaseSRWLockExclusive(&g_srwLock);
WakeConditionVariable(&g_cvConsume);
}
return ;
} unsigned int _stdcall ReaderThread(void* pParam)
{
g_nReaderCount++;
//printf("Enter readerthread-%d\n", g_nReaderCount);
while (TRUE)
{
Sleep();
//这里使用的例子和书中的例子有所不同,书中的例子中的ReaderThread仅仅是读取队列中的内容,而这里      //会去修改队列的内容,所以不能使用AcquireSRWLockShared.
AcquireSRWLockExclusive(&g_srwLock);
if (g_Queue.IsEmpty())
{
printf("Queue is empty..\n");
SleepConditionVariableSRW(&g_cvConsume, &g_srwLock, INFINITE, );
}
/*else
{ }*/
printf("Consume element:%d\n", g_Queue.DelElement());
ReleaseSRWLockExclusive(&g_srwLock);
WakeAllConditionVariable(&g_cvProduce);//don't use wakeconditionvariable().
}
return ;
} void main()
{
InitializeSRWLock(&g_srwLock);
HANDLE hWriter1 = (HANDLE)_beginthreadex(NULL, , WriterThread, NULL, , NULL);
HANDLE hWriter2 = (HANDLE)_beginthreadex(NULL, , WriterThread, NULL, , NULL);
HANDLE hReader1 = (HANDLE)_beginthreadex(NULL, , ReaderThread, NULL, , NULL);
HANDLE hReader2 = (HANDLE)_beginthreadex(NULL, , ReaderThread, NULL, , NULL);
HANDLE hReader3 = (HANDLE)_beginthreadex(NULL, , ReaderThread, NULL, , NULL);
HANDLE hArray[] = { hWriter1, hWriter2, hReader1, hReader2, hReader3 };
WaitForMultipleObjects(, hArray, TRUE, INFINITE);
CloseHandle(hWriter1);
CloseHandle(hWriter2);
CloseHandle(hReader1);
CloseHandle(hReader2);
CloseHandle(hReader3);
getchar(); }

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

  1. 【windows核心编程】 第八章 用户模式下的线程同步

    Windows核心编程 第八章 用户模式下的线程同步 1. 线程之间通信发生在以下两种情况: ①    需要让多个线程同时访问一个共享资源,同时不能破坏资源的完整性 ②    一个线程需要通知其他线程 ...

  2. windows核心编程---第八章 使用内核对象进行线程同步

    使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...

  3. windows核心编程---第七章 用户模式下的线程同步

    用户模式下的线程同步 系统中的线程必须访问系统资源,如堆.串口.文件.窗口以及其他资源.如果一个线程独占了对某个资源的访问,其他线程就无法完成工作.我们也必须限制线程在任何时刻都能访问任何资源.比如在 ...

  4. 《windows核心编程系列》七谈谈用户模式下的线程同步

    用户模式下的线程同步 系统中的线程必须访问系统资源,如堆.串口.文件.窗口以及其他资源.如果一个线程独占了对某个资源的访问,其他线程就无法完成工作.我们也必须限制线程在任何时刻都能访问任何资源.比如在 ...

  5. Windows核心编程:第8章 用户模式下的线程同步

    Github https://github.com/gongluck/Windows-Core-Program.git //第8章 用户模式下的线程同步.cpp: 定义应用程序的入口点. // #in ...

  6. 用户模式下的线程同步的分析(Windows核心编程)

    线程同步 同一进程或者同一线程可以生成许多不同的子线程来完成规定的任务,但是多个线程同时运行的情况下可能需要对某个资源进行读写访问,比如以下这个情况:创建两个线程对同一资源进行访问,最后打印出这个资源 ...

  7. Windows核心编程 第八章 用户方式中线程的同步(上)

    第8章 用户方式中线程的同步 当所有的线程在互相之间不需要进行通信的情况下就能够顺利地运行时, M i c r o s o f t Wi n d o w s的运行性能最好.但是,线程很少能够在所有的时 ...

  8. 第8章 用户模式下的线程同步(4)_条件变量(Condition Variable)

    8.6 条件变量(Condition Variables)——可利用临界区或SRWLock锁来实现 8.6.1 条件变量的使用 (1)条件变量机制就是为了简化 “生产者-消费者”问题而设计的一种线程同 ...

  9. 第8章 用户模式下的线程同步(1)_Interlocked系列函数

    8.1 原子访问:Interlocked系列函数(Interlock英文为互锁的意思) (1)原子访问的原理 ①原子访问:指的是一线程在访问某个资源的同时,能够保证没有其他线程会在同一时刻访问该资源. ...

随机推荐

  1. 提高eclipse使用效率(二)—— 提高Android开发效率的小技巧

    XML文件的代码提示 adt中也有xml文件的代码提示,为了让提示来的更加猛烈,我们还要设置一下 打开eclipse - Window - Preferences,在右边的目录树中切换到XML - X ...

  2. lnmp 一键安装包

    系统需求: CentOS/RHEL/Fedora/Debian/Ubuntu/Raspbian Linux系统 需要5GB以上硬盘剩余空间 需要128MB以上内存(如果为128MB的小内存VPS,Xe ...

  3. 大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础

    第五章 函数式编程-基础5.1 函数式编程内容说明5.1.1 函数式编程内容5.1.2 函数式编程授课顺序5.2 函数式编程介绍5.2.1 几个概念的说明5.2.2 方法.函数.函数式编程和面向对象编 ...

  4. html5那些事儿

    一.优势1.标签的改变:<header>,<footer>,<dialog>,<aside>,<figure>,<section> ...

  5. collection 和 collections

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha collection 是集合的意思. 集合 是 集合类的上级接口, 比如 set 和 l ...

  6. CF1042C Array Product 分类讨论+贪心

    考虑有无负数(负数的个数为奇视作“有”,否则为“无”)和有无零 无负数无零,全部合并即可 无负数有零,那么把零合并起来,删掉零 有负数无零,把最大的负数找出来,删掉,合并剩余的数 有负数有零,把零和最 ...

  7. [BZOJ4561][JLOI2016]圆的异或并(扫描线)

    考虑任何一条垂直于x轴的直线,由于圆不交,所以这条直线上的圆弧构成形似括号序列的样子,且直线移动时圆之间的相对位置不变. 将每个圆拆成两边,左端加右端删.每次加圆时考虑它外面最内层的括号属于谁.用se ...

  8. BZOJ 2843: 极地旅行社 lct splay

    http://www.lydsy.com/JudgeOnline/problem.php?id=2843 https://blog.csdn.net/clove_unique/article/deta ...

  9. 2018-2019-2 20162318《网络对抗技术》Exp4 恶意代码分析

    一.实验目标 监控你自己系统的运行状态,看有没有可疑的程序在运行. 分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具尽量使用原生指令或sysinternals,systracer套件 ...

  10. 鸟哥的私房菜:Linux文件与目录管理

    1. 目录与路径 1.1 相对路径与绝对路径           绝对路径:路径的写法『一定由根目录 / 写起』,如:/usr/share/doc 这个目录.     相对路径:路径的写法『不是由 / ...