下面起了两个线程,每个对一个全局变量加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. C++11线程池的实现

    什么是线程池 处理大量并发任务,一个请求一个线程来处理请求任务,大量的线程创建和销毁将过多的消耗系统资源,还增加了线程上下文切换开销. 线程池通过在系统中预先创建一定数量的线程,当任务请求到来时从线程 ...

  2. 8-1 binpacking uva1149(贪心)

    题意:给定N个物品的重量Li  背包的容量M 同时要求每个背包最多装两个物品 求至少要多少个背包才能装下所有物品 简单贪心  注意输出: #include<bits/stdc++.h> u ...

  3. Dubbo的直接提供者

    1.场景 在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从 ...

  4. h5行情k线开发

    前言         由于公司项目需要,要做港股行情的H5版本,经过分析需求,大致有两块难点: 一是行情的推送接收,二是行情K线的生成及相关操作.本文章主要分析行情K线的相关实现,由于我们前端团队之前 ...

  5. java jdbc深入理解(connection与threadlocal与数据库连接池和事务实)

    1.jdbc连接数据库,就这样子 Class.forName("com.mysql.jdbc.Driver");java.sql.Connection conn = DriverM ...

  6. 二、django rest_framework源码之认证流程剖析

    1 绪言 上一篇中讲了django rest_framework总体流程,整个流程中最关键的一步就是执行dispatch方法.在dispatch方法中,在调用了一个initial方法,所有的认证.权限 ...

  7. django中两张表有外键关系的相互查找方法,自定义json编码方法

    两张通过外键联系的表,如何在一张表上根据另一张表上的属性查找满足条件的对象集? 平常查找表中数据的条件是python中已有的数据类型,通过名字可以直接查找.如果条件是表中外键列所对应表的某一列,该如何 ...

  8. 富文本插件KindEditor

    具体用法查看官网http://kindeditor.net/doc.php {% load staticfiles %} <!DOCTYPE html> <html lang=&qu ...

  9. redis keys 命令

    ## 删除存在的key del key ## 序列体弱给定key,并返回被序列化的值 dump key ## 检查key是否存在 exists key ## 为给定key设置过期时间 expire k ...

  10. Windows系统php5.6安装Imagick库

    Windows上的安装坑比较多 1.安装Imagick,需要下载6.9.3之下版本的 http://imagemagick.org/script/download.php 官网都是新版本不可以用 我安 ...