window下线程同步之(Event Objects(事件))
Event 方式是最具弹性的同步机制,因为他的状态完全由你去决定,不会像 Mutex 和 Semaphores 的状态会由类似:WaitForSingleObject 一类的函数的调用而改变,所以你可以精确的告诉 Event 对象该做什么事?以及什么时候去做!
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName // object name
);
lpEventAttributes : 一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。
bManualReset : 创建一个人工重置的事件(TRUE)使用ResetEvent()手动重置为无信号状态,
创建一个自动重置的事件(FALSE)。当一个等待线程被释放时,自动重置状态为无信号状态。
bInitialState : 用于指明该事件是要初始化为已通知状态(TRUE)还是未通知状态(FALSE)
bManualReset为TRUE时: 人工重置事件,当一个等待线程被释放时,必须使用ResetEvent()手动重置为无型号状态
当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。
bManualReset为FALSE时: 自动重置事件,当一个等待线程被释放时,自动重置状态为无信号状态
当自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
自动重置事件(通常没有必要为自动重置的事件调用ResetEvent函数)。
使用方法:
1、创建一个事件对象:CreateEvent;
2、打开一个已经存在的事件对象:OpenEvent;
3、获得事件的占有权:WaitForSingleObject 等函数(可能造成阻塞);
4、释放事件的占有权(设置为激发(有信号)状态,以让其他等待中的线程苏醒):SetEvent;
5、手动置为非激发(无信号)状态:ResetEvent
6、关闭事件对象句柄:CloseHandle;
固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、通常被用于 overlapped I/O 或被用来设计某些自定义的同步对象。
相关函数:
BOOL WINAPI SetEvent( __in HANDLE hEvent ); 把event对象设置为激活状态 BOOL WINAPI ResetEvent( __in HANDLE hEvent ); 把event对象设置为非激活状态 BOOL WINAPI PulseEvent( __in HANDLE hEvent );
如果是一个人工重置事件:把event对象设置为激活状态,唤醒“所有”等待中的线程,然后event恢复为非激活状态
如果是一个自动重置事件:把event对象设置为激活状态,唤醒 “一个”等待中的线程,然后event恢复为非激活状态
下面主要演示一下采用CreateEvent实现线程同步。
例子很简单,主要测试CreateEvent中bManualReset 和 bInitialState 参数的取值在线程调用中信号状态的情况。
1、bManualReset:TRUE
bInitialState:TRUE
CreateEvent(NULL, TRUE, TRUE, NULL); //人工重置事件:使用手动重置为无信号状态,初始化时有信号状态
#include <iostream>
#include <windows.h>
using namespace std; DWORD WINAPI ThreadProc1(LPVOID lpParam);
DWORD WINAPI ThreadProc2(LPVOID lpParam); HANDLE hEvent = NULL;
HANDLE hThread1 = NULL;
HANDLE hThread2 = NULL; int main(int argc, char *args[])
{ hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); //使用手动重置为无信号状态,初始化时有信号状态 hThread1 = CreateThread(NULL, , (LPTHREAD_START_ROUTINE)ThreadProc1, NULL, ,NULL);
hThread2 = CreateThread(NULL, , (LPTHREAD_START_ROUTINE)ThreadProc2, NULL, ,NULL); WaitForSingleObject( hThread1, INFINITE );
WaitForSingleObject( hThread2,INFINITE ); return ;
}
DWORD WINAPI ThreadProc1(LPVOID lpParam)
{ if ( WAIT_OBJECT_0 == WaitForSingleObject(hEvent,INFINITE) )
{
cout <<"线程1被调用!\n";
ResetEvent(hEvent);
} return ;
}
DWORD WINAPI ThreadProc2(LPVOID lpParam)
{
if ( WAIT_OBJECT_0 == WaitForSingleObject(hEvent,INFINITE) )
{
cout <<"线程2被调用!\n";
ResetEvent(hEvent);
}
return ;
}
2、bManualReset:TRUE
bInitialState:FALSE
CreateEvent(NULL, TRUE, FALSE, NULL);//人工重置事件:使用手动重置为无信号状态,初始化时为无信号状态
#include <iostream>
#include <windows.h>
using namespace std; DWORD WINAPI ThreadProc1(LPVOID lpParam);
DWORD WINAPI ThreadProc2(LPVOID lpParam); HANDLE hEvent = NULL;
HANDLE hThread1 = NULL;
HANDLE hThread2 = NULL; int main(int argc, char *args[])
{ hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //使用手动重置为无信号状态,初始化时无信号状态 hThread1 = CreateThread(NULL, , ThreadProc1, NULL, ,NULL);
hThread2 = CreateThread(NULL, , ThreadProc2, NULL, ,NULL); WaitForSingleObject( hThread1, INFINITE );
WaitForSingleObject( hThread2,INFINITE ); return ;
}
DWORD WINAPI ThreadProc1(LPVOID lpParam)
{ if ( WAIT_OBJECT_0 == WaitForSingleObject(hEvent,INFINITE) )
{
cout <<"线程1被调用!\n";
} return ;
}
DWORD WINAPI ThreadProc2(LPVOID lpParam)
{
if ( WAIT_OBJECT_0 == WaitForSingleObject(hEvent,INFINITE) )
{
cout <<"线程2被调用!\n";
}
return ;
}
当创建手动重置事件时初始化为无信号 hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 得到的结果是:

在hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 之后添加
SetEvent( hEvent );设置为有信号,因为bManualReset为TRUE时,等待该事件的所有线程均变为可调度线程

当我们在线程一中添加ResetEvent(hEvent);时运行程序发现线程1被调用,线程2没有被调用:

3、
bManualReset:FALSE
bInitialState:TRUE
CreateEvent(NULL, FALSE, TRUE, NULL); //自动重置事件:当一个等待线程被释放时,自动重置为无信号状态,初始是有信号状态
#include <iostream>
#include <windows.h>
using namespace std; DWORD WINAPI ThreadProc1(LPVOID lpParam);
DWORD WINAPI ThreadProc2(LPVOID lpParam); HANDLE hEvent = NULL;
HANDLE hThread1 = NULL;
HANDLE hThread2 = NULL; int main(int argc, char *args[])
{ hEvent = CreateEvent(NULL, FALSE, TRUE, NULL); //使用自动重置为无信号状态,初始化时有信号状态 hThread1 = CreateThread(NULL, , ThreadProc1, NULL, ,NULL);
hThread2 = CreateThread(NULL, , ThreadProc2, NULL, ,NULL); WaitForSingleObject( hThread1, INFINITE );
WaitForSingleObject( hThread2,INFINITE ); return ;
}
DWORD WINAPI ThreadProc1(LPVOID lpParam)
{ if ( WAIT_OBJECT_0 == WaitForSingleObject(hEvent,INFINITE) )
{
cout <<"线程1被调用!\n";
}
return ;
}
DWORD WINAPI ThreadProc2(LPVOID lpParam)
{
if ( WAIT_OBJECT_0 == WaitForSingleObject(hEvent,INFINITE) )
{
cout <<"线程2被调用!\n";
}
return ;
}

从结果可以看到线程1被调用,线程2一直在等待。由于CreateEvent(NULL, FALSE, TRUE, NULL)//使用自动重置为无信号状态,初始化时有信号状态
所以当线程1执行的时候hEvent是有信号的,线程1正常运行,又由于bManualReset为FALSE时:当一个等待线程被释放时,自动重置状态为无信号状态
因此线程2一直在等待,由于主线程加了WaitForSingleObject( hThread2,INFINITE ); 所以主线程也在一直等待
4、
bManualReset:FALSE
bInitialState:FALSE
CreateEvent(NULL, FALSE, FALSE, NULL);//自动重置事件:线程释放后自动重置为无信号状态,初始化时为无信号状态
#include <iostream>
#include <windows.h>
using namespace std; DWORD WINAPI ThreadProc1(LPVOID lpParam);
DWORD WINAPI ThreadProc2(LPVOID lpParam); HANDLE hEvent = NULL;
HANDLE hThread1 = NULL;
HANDLE hThread2 = NULL; int main(int argc, char *args[])
{ hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //使用自动重置为无信号状态,初始化时无信号状态
SetEvent(hEvent); hThread1 = CreateThread(NULL, , ThreadProc1, NULL, ,NULL);
hThread2 = CreateThread(NULL, , ThreadProc2, NULL, ,NULL); WaitForSingleObject( hThread1, INFINITE );
WaitForSingleObject( hThread2,INFINITE ); return ;
}
DWORD WINAPI ThreadProc1(LPVOID lpParam)
{ if ( WAIT_OBJECT_0 == WaitForSingleObject(hEvent,INFINITE) )
{
cout <<"线程1被调用!\n";
}
return ;
}
DWORD WINAPI ThreadProc2(LPVOID lpParam)
{
if ( WAIT_OBJECT_0 == WaitForSingleObject(hEvent,INFINITE) )
{
cout <<"线程2被调用!\n";
}
return ;
}

由于CreateEvent(NULL, FALSE, FALSE, NULL);//使用手动重置为无信号状态,初始化时为无信号状态
由于调用SetEvent,hEvent为有信号状态,线程1正常执行,又由于bManualReset为FALSE时: 当一个等待线程被释放时,自动重置状态为无信号状态,调用完线程1后,hEvent自动重置为无信号状态,所以线程2只能在等待
window下线程同步之(Event Objects(事件))的更多相关文章
- window下线程同步之(原子锁)
原子锁:当多个线程同时对同一资源进行操作时,由于线程间资源的抢占,会导致操作的结果丢失或者不是我们预期的结果. 比如:线程A对一个变量进行var++操作,线程B也执行var++操作,当线程A执行var ...
- window下线程同步之(Mutex(互斥器) )
使用方法: 1.创建一个互斥器:CreateMutex: 2.打开一个已经存在的互斥器:OpenMutex: 3.获得互斥器的拥有权:WaitForSingleObject.WaitForMultip ...
- window下线程同步之(Semaphores(信号量))
HANDLE WINAPI CreateSemaphore( _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes _In_ LONG lIniti ...
- window下线程同步之(Critical Sections(关键代码段、关键区域、临界区域)
关键区域(CriticalSection) 临界区是为了确保同一个代码片段在同一时间只能被一个线程访问,与原子锁不同的是临界区是多条指令的锁定,而原子锁仅仅对单条操作指令有效;临界区和原子锁只能控制同 ...
- EventStore .NET API Client在使用线程池线程同步写入Event导致EventStore连接中断的问题研究
最近,在使用EventStore的.NET Client API采用大量线程池线程同步写入Event时(用于模拟ASP.NET服务端大并发写入Event的情况),发现EventStore的连接会随机中 ...
- 孤荷凌寒自学python第四十一天python的线程同步之Event对象
孤荷凌寒自学python第四十一天python的线程同步之Event对象 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 鉴于Lock锁与RLock锁均宣告没有完全完成同步文件操作的问题,于 ...
- 线程同步(windows平台):事件
一:介绍 事件Event实际上是个内核对象,事件分两种状态:激发状态和未激发状态.分两种类型:手动处置事件和自动处置事件.手动处置事件被设置为激发状态后,会唤醒所有等待的线程,一直保持为激发状态,直到 ...
- Delphi多线程编程--线程同步的方法(事件、互斥、信号、计时器)简介
更详细的可以参考:http://www.cnblogs.com/xumenger/p/4450659.html 或者参考之后的博客 四个系统内核对象(事件.互斥.信号.计时器)都是线程同步的手段,从这 ...
- 线程同步——用户模式下线程同步——Slim读写锁实现线程同步
//Slim读/写锁实现线程同步 SRWlock 的目的和关键段相同:对同一资源进行保护,不让其它线程访问. 但是,与关键段不同的是,SRWlock允许我们区分哪些想要读取资源的线程(读取者线程) 和 ...
随机推荐
- 【bzoj3687】简单题
#3687. 简单题 内存限制:512 MiB时间限制:10 Sec 提交提交记录讨论 题目描述 小呆开始研究集合论了,他提出了关于一个数集四个问题:1.子集的异或和的算术和.2.子集的异或和的异或和 ...
- 【bzoj3230】相似子串
Portal -->bzoj3230 Description 给你一个长度为\(n\)的字符串,把它的所有本质不同的子串按字典序大小排序,有\(m\)个询问,对于每一个询问\(x,y\)你需要回 ...
- css等比例分割父级容器(完美三等分)
html部分代码: 方法一: 浮动布局+百分比 (将子元素依次左浮动,根据子元素的个数,设定每个子元素的宽度百分比) 方法二:行内元素(inline-block)+百分比 方法三: 父元素 disp ...
- mysql命令修改登录用户密码
方法1: 用SET PASSWORD命令 首先登录MySQL. 格式:mysql> set password for 用户名@localhost = password(‘新密码’); 例子:my ...
- 进程间共享数据Manager
一.前言 进程间的通信Queue()和Pipe(),可以实现进程间的数据传递.但是要使python进程间共享数据,我们就要使用multiprocessing.Manager. Manager()返回的 ...
- 分享 koa + mysql 的开发流程,构建 node server端,一次搭建个人博客
前言 由于一直在用 vue 写业务,为了熟悉下 react 开发模式,所以选择了 react.数据库一开始用的是 mongodb,后来换成 mysql 了,一套下来感觉 mysql 也挺好上手的.re ...
- HTML5笔记-加强版
新增的语法结构表单验证 1.新的页面结构以及宽松的语法规范:<!doctype html> <meta charset=“utf-8”/> 2.新的结构化元素:语义化标签: ...
- CF821 B. Okabe and Banana Trees 简单数学
Link 题意:给出一条直线,在直线上取一点,其垂直x,y轴作成一个,求矩阵中所有包含的点的x,y坐标之和的最大值. 思路:对于一个任意一点我们计算公式,对于任意一点$(x, y)$,有$(x+y)^ ...
- 816B. Karen and Coffee 前缀和思维 或 线段树
LINK 题意:给出n个[l,r],q个询问a,b,问被包含于[a,b]且这样的区间数大于k个的方案数有多少 思路:预处理所有的区间,对于一个区间我们标记其(左边界)++,(右边界+1)--这样就能通 ...
- Disruptor的使用
..................2015年的第一天................... 本文代码托管在 https://github.com/hupengcool/disruptor-start ...