《Windows核心编程》第九章——用内核对象进行线程同步
先举一个有bug的例子:
#include <iostream>
#include <windows.h>
#include <process.h> using namespace std;
#define MAX_SIZE 0x500 HANDLE g_hSubmit;
HANDLE g_hReturn;
HANDLE g_hStop;
char g_szInput[MAX_SIZE] = {}; unsigned int _stdcall ThreadServer(void* pParam)
{
while (TRUE)
{
int nWaitRes = WaitForSingleObject(g_hStop, 100);
if (WAIT_OBJECT_0 == nWaitRes)
break; WaitForSingleObject(g_hSubmit, INFINITE);
printf("Recieve:%s\n", g_szInput);
SetEvent(g_hReturn);
}
SetEvent(g_hStop);
printf("Set stop\n");
return ;
} void main()
{
int count = ;
g_hSubmit = CreateEvent(NULL, FALSE, FALSE, NULL);
g_hReturn = CreateEvent(NULL, FALSE, FALSE, NULL);
g_hStop = CreateEvent(NULL, FALSE, FALSE, NULL); HANDLE hTheadServer = (HANDLE)_beginthreadex(NULL, , ThreadServer, NULL, , NULL);
while (TRUE)
{
count++;
printf("Input:");
cin.getline(g_szInput, MAX_SIZE);
SetEvent(g_hSubmit);
WaitForSingleObject(g_hReturn, INFINITE);
if (count == ){
Sleep();
SetEvent(g_hStop); break;
}
} HANDLE hHandle[];
hHandle[] = g_hStop;
hHandle[] = hTheadServer;
WaitForMultipleObjects(, hHandle, TRUE, INFINITE);
CloseHandle(hTheadServer);
CloseHandle(g_hReturn);
CloseHandle(g_hStop);
CloseHandle(g_hSubmit);
getchar(); }
起初,我想要设置一个事件——g_hStop来通知线程,使得ThreadServer线程能够被主线程停止,但是这里出现了一个问题,如果我刻意让主线程Sleep2秒再去SetEvent,那么等待g_hStop的wait函数就会超时,从而往下继续执行等待Input,而此时主线程已经退出input循环,那么就会死锁。所以我改为使用全局变量来使得Threadserver线程退出:
#include <iostream>
#include <windows.h>
#include <process.h> using namespace std;
#define MAX_SIZE 0x500 HANDLE g_hSubmit;
HANDLE g_hReturn;
HANDLE g_hStop;
int g_nCount = ;
char g_szInput[MAX_SIZE] = {}; unsigned int _stdcall ThreadServer(void* pParam)
{
while (TRUE)
{
if (g_nCount == )
break; WaitForSingleObject(g_hSubmit, INFINITE);
printf("Recieve:%s\n", g_szInput);
SetEvent(g_hReturn);
}
SetEvent(g_hStop);
printf("Set stop\n");
return ;
} void main()
{
g_hSubmit = CreateEvent(NULL, FALSE, FALSE, NULL);
g_hReturn = CreateEvent(NULL, FALSE, FALSE, NULL);
g_hStop = CreateEvent(NULL, FALSE, FALSE, NULL); HANDLE hTheadServer = (HANDLE)_beginthreadex(NULL, , ThreadServer, NULL, , NULL);
while (TRUE)
{
g_nCount++;
printf("Input:");
cin.getline(g_szInput, MAX_SIZE);
SetEvent(g_hSubmit);
WaitForSingleObject(g_hReturn, INFINITE);
if (g_nCount == ){
Sleep();
SetEvent(g_hStop); break;
}
} HANDLE hHandle[];
hHandle[] = g_hStop;
hHandle[] = hTheadServer;
WaitForMultipleObjects(, hHandle, TRUE, INFINITE);
CloseHandle(hTheadServer);
CloseHandle(g_hReturn);
CloseHandle(g_hSubmit);
getchar(); }

三种方式改进:
1、如果你非要使用第一种情况,那么请把等待时间设置的长一些,不要是100毫秒,起码要是等待十秒,确保事件触发后,不会超时。
2、在代码设计的时候,就不要在while中使用两个waitforsingleobject,这种设计就给死锁带来了可能性:

3、使用waitformultiobject等待两个event之一,然后判断等到的是哪个,从而决定来做什么

一个Mutex和semaphore合用的例子:
#include <iostream>
#include <windows.h>
#include <vector>
#include <process.h> using namespace std; #define MAX_SIZE 10 long g_ServerCount = ; class CQueue{
public:
CQueue();
~CQueue(); void Append();
void Remove();
private:
vector<int> m_vecQueue;
HANDLE m_hMutex;
HANDLE m_hSem;
HANDLE m_hHandles[];
}; CQueue g_c; CQueue::CQueue()
{
m_hMutex = CreateMutex(NULL, FALSE, NULL);
m_hSem = CreateSemaphore(NULL, , , NULL);
m_hHandles[] = m_hMutex;
m_hHandles[] = m_hSem;
} CQueue::~CQueue()
{
CloseHandle(m_hMutex);
CloseHandle(m_hSem);
} void CQueue::Append(){
DWORD dwRet = WaitForSingleObject(m_hMutex, INFINITE);
InterlockedExchangeAdd(&g_ServerCount, );
if (dwRet == WAIT_OBJECT_0)
{
LONG lPrevCount;
int bRet = ReleaseSemaphore(m_hSem, , &lPrevCount);
if (bRet)
{
m_vecQueue.push_back(g_ServerCount);
printf("Add element %d\n", g_ServerCount);
}
}
ReleaseMutex(m_hMutex);
} void CQueue::Remove()
{
DWORD dwRet = WaitForMultipleObjects(, m_hHandles, TRUE, INFINITE);
if (WAIT_OBJECT_0 == dwRet)
{
printf("Remove element %d\n", m_vecQueue.back());
m_vecQueue.pop_back();
}
ReleaseMutex(m_hMutex);
} unsigned int _stdcall ServerThread(void* pParam)
{
while (TRUE)
{
Sleep();
g_c.Append();
} return ;
} unsigned int _stdcall ClientThread(void* pParam)
{
while (TRUE)
{
Sleep();
g_c.Remove();
} return ;
} void main()
{
HANDLE h_Handles[];
h_Handles[] = (HANDLE)_beginthreadex(NULL, , ServerThread, NULL, , NULL);
h_Handles[] = (HANDLE)_beginthreadex(NULL, , ServerThread, NULL, , NULL);
h_Handles[] = (HANDLE)_beginthreadex(NULL, , ClientThread, NULL, , NULL);
WaitForMultipleObjects(_countof(h_Handles), h_Handles, TRUE, INFINITE);
for (int i = ; i < _countof(h_Handles); i++)
CloseHandle(h_Handles[i]);
getchar();
}
《Windows核心编程》第九章——用内核对象进行线程同步的更多相关文章
- windows核心编程---第八章 使用内核对象进行线程同步
使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...
- Windows核心编程:第9章 用内核对象进行线程同步
Github https://github.com/gongluck/Windows-Core-Program.git //第9章 用内核对象进行线程同步.cpp: 定义应用程序的入口点. // #i ...
- Windows核心编程 第九章 线程与内核对象的同步(下)
9.4 等待定时器内核对象 等待定时器是在某个时间或按规定的间隔时间发出自己的信号通知的内核对象.它们通常用来在某个时间执行某个操作. 若要创建等待定时器,只需要调用C r e a t e Wa i ...
- Windows核心编程 第九章 线程与内核对象的同步(上)
第9章 线程与内核对象的同步 上一章介绍了如何使用允许线程保留在用户方式中的机制来实现线程同步的方法.用户方式同步的优点是它的同步速度非常快.如果强调线程的运行速度,那么首先应该确定用户方式的线程同步 ...
- windows核心编程---第九章 同步设备IO与异步设备IO之同步IO
同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...
- 第9章 用内核对象进行线程同步(4)_死锁(DeadLock)及其他
9.7 线程同步对象速查表 对象 何时处于未触发状态 何时处于触发状态 成功等待的副作用 进程 进程仍在运行的时候 进程终止的时(ExitProcess.TerminateProcess) 没有 线程 ...
- 第9章 用内核对象进行线程同步(3)_信号量(semaphore)、互斥对象(mutex)
9.5 信号量内核对象(Semaphore) (1)信号量的组成 ①计数器:该内核对象被使用的次数 ②最大资源数量:标识信号量可以控制的最大资源数量(带符号的32位) ③当前资源数量:标识当前可用资源 ...
- 第9章 用内核对象进行线程同步(2)_可等待计时器(WaitableTimer)
9.4 可等待的计时器内核对象——某个指定的时间或每隔一段时间触发一次 (1)创建可等待计时器:CreateWaitableTimer(使用时应把常量_WIN32_WINNT定义为0x0400) 参数 ...
- 第9章 用内核对象进行线程同步(1)_事件对象(Event)
9.1 等待函数 (1)WaitForSingleObject(hObject,dwMilliseonds); ①dwMilliseconds为INFINITE时表示无限等待 ②dwMilliseco ...
随机推荐
- 【LOJ】#2037. 「SHOI2015」脑洞治疗仪
题解 维护区间内1的个数,左边数0的长度,右边数0的长度,区间内0区间最长个数,覆盖标记 第一种操作区间覆盖0 第二种操作查询\([l_0,r_0]\)中1的个数,区间覆盖0,然后覆盖时找到相对应的区 ...
- js判断某年某月有多少天
function getCountDays(ym) { var curDate = new Date(ym); /* 获取当前月份 */ var curMonth = curDate.getMonth ...
- ICMP隧道工具ptunnel
ICMP隧道工具ptunnel 在一些网络环境中,如果不经过认证,TCP和UDP数据包都会被拦截.如果用户可以ping通远程计算机,就可以尝试建立ICMP隧道,将TCP数据通过该隧道发送,实现不受 ...
- bootm命令移植
<bootm作用> 为linux内核的启动准备条件 <bootloader作用总结> (1)初始化软/硬件(内存硬件/外部设备/堆栈) (2)启动操作系统 <uImagi ...
- jQuery学习总结1
一.下载集CDN引入 1.1.官方下载 地址:http://jQuery.com/download/ jq自2.0版本开始,不再支持IE9一下浏览器:自3.0版本开始,针对移动端做了优化处理: 引入 ...
- [CodeVS1243]网络提速
题目大意: 有n个点的连通图,有m次可以将某一条边权值减半的机会. 不同的机会可以叠加作用于同一条边. 求1~n的最短路. 思路: 拆点,记录1到每个点在使用不同次数的机会后的最短路,然后直接跑Dij ...
- gradle/maven/eclipse工程相互转化
原文: gradle/maven/eclipse工程相互转化 gradle/maven/eclipse工程相互转化:前提安装好相应的工具和插件.1.Maven->eclipse mvn ecl ...
- bzoj 2555
暴力. 收获: 1.第一道后缀自动机,大概知道怎么写了,有一些原理性的东西还要理解. 2.计算right集合的大小 /***************************************** ...
- 【BZOJ-1396&2865】识别子串&字符串识别 后缀自动机/后缀树组 + 线段树
1396: 识别子串 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 312 Solved: 193[Submit][Status][Discuss] ...
- BZOJ 2756: [SCOI2012]奇怪的游戏 网络流/二分
2756: [SCOI2012]奇怪的游戏 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 1594 Solved: 396[Submit][Stat ...