下面起了两个线程,每个对一个全局变量加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. 浅谈ABP最佳实践

    目录 ABP概念简述 ABP在[事务操作]上的简便性 ABP在[关联查询]上的“美”和“坑” ABP的[参数验证]方式 ABP概念简述 ABP是“ASP.NET Boilerplate Project ...

  2. day1作业:登录接口

    作业一:编写登陆接口 1.输入用户名和密码 2.认证成功后显示欢迎信息 3.输错三次后锁定 思路: (1)用户输入用户名: (2)去锁定文件中验证用户名是否锁定: (3)去当前用户验证用户是否存在: ...

  3. 005 Ajax中使用jquery实现三种格式的信息

    1.jquery中的ajax 二:load 2.load方法 3.load测试程序大纲 4.load测试程序 <!DOCTYPE html> <html> <head&g ...

  4. (转)python随机数用法

    进行以下操作前先 import random ,导入random模块 1. random.seed(int) 给随机数对象一个种子值,用于产生随机序列. 对于同一个种子值的输入,之后产生的随机数序列也 ...

  5. MySQL用户授权 和 bin-log日志 详解和实战

    看了上一篇博文的发布时间,到目前已经有三个月没更新博文了.这三个月经历了很多事情,包括工作.生活和感情等等.由于个人发展的原因,这个月准备换工作啦.在这段时间,我会把Web大型项目中所接触到的技术都总 ...

  6. 20169211《Linux内核原理与分析》课程总结

    第一周作业:linux入门学习:熟悉操作linux的基础命令 第二周作业:实验反汇编一个简答的C程序,学习汇编代码的工作过程 第三周作业:学习linux内核的启动过程 第四周作业:学习linux内核进 ...

  7. CSUOJ 1005 Binary Search Tree analog

    Description Binary Search Tree, abbreviated as BST, is a kind of binary tree maintains the following ...

  8. 基于TensorFlow Serving的深度学习在线预估

    一.前言 随着深度学习在图像.语言.广告点击率预估等各个领域不断发展,很多团队开始探索深度学习技术在业务层面的实践与应用.而在广告CTR预估方面,新模型也是层出不穷: Wide and Deep[1] ...

  9. ASP.NET MVC 提高运行速度的几种性能优化方法

    主要介绍ASP.NETMVC 应用提速的六种方法,因为没有人喜欢等待,所以介绍几种常用的优化方法. 大家可能会遇到排队等待,遇到红灯要等待,开个网页要等待,等等等. 理所当然,没有人喜欢等待网页慢吞吞 ...

  10. [ 转载 ] Mysql 数据库常用命令

    完整的创建数据库例子: >create database db_test default character set utf8 collate utf8_general_ci; >use ...