《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 ...
随机推荐
- java8 - 多线程时间安全问题
import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.format.DateTimeForma ...
- C语言:输入10个整数,找出其中绝对值最小的数
1 输入10个整数,找出其中绝对值最小的数(10分) 题目描述 输入10个整数,找出其中绝对值最小的数 输入 十个整数 输出 绝对值最小的数 样例输入 -10 -2 30 40 50 60 70 80 ...
- 【LOJ】#2443. 「NOI2011」智能车比赛
题解 显然是个\(n^2\)的dp 我们要找每个点不穿过非赛道区域能到达哪些区域的交点 可以通过控制两条向量负责最靠下的上边界,和最靠上的下边界,检查当前点在不在这两条向量之间即可,对于每个点可以\( ...
- linux查找文件或目录命令
inux查找文件或目录命令,前提:知道文件或者目录的具体名字,例如:sphinx.conf find 查找 find / -name dirname 查找目录 find -name filenam ...
- Python 多线程 实例
多线程实例 import threading import time def eat(): eatTime = time.time() for i in range(30): print('count ...
- 使用 Python 可以做什么?
翻译自 <Python学习手册(第5版)> Systems Programming Python 对操作系统服务的内置接口使其非常适合编写可移植.可维护的系统管理工具和实用程序 utili ...
- Java 内存模型基础
一.并发编程模型的两个关键问题 1. 线程之间如何通信 通信是指线程之间以何种机制来交换信息. 在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序 ...
- Codeforces.714D.Searching Rectangles(交互 二分)
题目链接 \(Description\) 在一个\(n*n\)的二维平面中有两个不相交的整点矩形,每次可以询问两个矩形有几个完全在你给出的一个矩形中.200次询问内确定两个矩形坐标. \(Soluti ...
- python开发_tkinter_菜单的不同选项
python的tkinter模块中,菜单也可以由你自定义你的风格 下面是我做的demo 运行效果: ====================================== 代码部分: ===== ...
- redis学习之一 - linux下安装配置
Content 0.序 1.如何安装? 2.配置参数及其意义 3.设为linux服务 0.序 本文主要是记录Redis在 Centos下的安装配置 .文中如无特别说明.表示redis-3.2.10代码 ...