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)

bManualResetTRUE时:  人工重置事件,当一个等待线程被释放时,必须使用ResetEvent()手动重置为无型号状态

当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。

bManualResetFALSE时: 自动重置事件,当一个等待线程被释放时,自动重置状态为无信号状态
                                     当自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。

自动重置事件(通常没有必要为自动重置的事件调用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(事件))的更多相关文章

  1. window下线程同步之(原子锁)

    原子锁:当多个线程同时对同一资源进行操作时,由于线程间资源的抢占,会导致操作的结果丢失或者不是我们预期的结果. 比如:线程A对一个变量进行var++操作,线程B也执行var++操作,当线程A执行var ...

  2. window下线程同步之(Mutex(互斥器) )

    使用方法: 1.创建一个互斥器:CreateMutex: 2.打开一个已经存在的互斥器:OpenMutex: 3.获得互斥器的拥有权:WaitForSingleObject.WaitForMultip ...

  3. window下线程同步之(Semaphores(信号量))

    HANDLE WINAPI CreateSemaphore( _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes _In_ LONG lIniti ...

  4. window下线程同步之(Critical Sections(关键代码段、关键区域、临界区域)

    关键区域(CriticalSection) 临界区是为了确保同一个代码片段在同一时间只能被一个线程访问,与原子锁不同的是临界区是多条指令的锁定,而原子锁仅仅对单条操作指令有效;临界区和原子锁只能控制同 ...

  5. EventStore .NET API Client在使用线程池线程同步写入Event导致EventStore连接中断的问题研究

    最近,在使用EventStore的.NET Client API采用大量线程池线程同步写入Event时(用于模拟ASP.NET服务端大并发写入Event的情况),发现EventStore的连接会随机中 ...

  6. 孤荷凌寒自学python第四十一天python的线程同步之Event对象

     孤荷凌寒自学python第四十一天python的线程同步之Event对象 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 鉴于Lock锁与RLock锁均宣告没有完全完成同步文件操作的问题,于 ...

  7. 线程同步(windows平台):事件

    一:介绍 事件Event实际上是个内核对象,事件分两种状态:激发状态和未激发状态.分两种类型:手动处置事件和自动处置事件.手动处置事件被设置为激发状态后,会唤醒所有等待的线程,一直保持为激发状态,直到 ...

  8. Delphi多线程编程--线程同步的方法(事件、互斥、信号、计时器)简介

    更详细的可以参考:http://www.cnblogs.com/xumenger/p/4450659.html 或者参考之后的博客 四个系统内核对象(事件.互斥.信号.计时器)都是线程同步的手段,从这 ...

  9. 线程同步——用户模式下线程同步——Slim读写锁实现线程同步

    //Slim读/写锁实现线程同步 SRWlock 的目的和关键段相同:对同一资源进行保护,不让其它线程访问. 但是,与关键段不同的是,SRWlock允许我们区分哪些想要读取资源的线程(读取者线程) 和 ...

随机推荐

  1. 【bzoj3687】简单题

    #3687. 简单题 内存限制:512 MiB时间限制:10 Sec 提交提交记录讨论 题目描述 小呆开始研究集合论了,他提出了关于一个数集四个问题:1.子集的异或和的算术和.2.子集的异或和的异或和 ...

  2. 【bzoj3230】相似子串

    Portal -->bzoj3230 Description 给你一个长度为\(n\)的字符串,把它的所有本质不同的子串按字典序大小排序,有\(m\)个询问,对于每一个询问\(x,y\)你需要回 ...

  3. css等比例分割父级容器(完美三等分)

    html部分代码: 方法一: 浮动布局+百分比 (将子元素依次左浮动,根据子元素的个数,设定每个子元素的宽度百分比) 方法二:行内元素(inline-block)+百分比 方法三: 父元素  disp ...

  4. mysql命令修改登录用户密码

    方法1: 用SET PASSWORD命令 首先登录MySQL. 格式:mysql> set password for 用户名@localhost = password(‘新密码’); 例子:my ...

  5. 进程间共享数据Manager

    一.前言 进程间的通信Queue()和Pipe(),可以实现进程间的数据传递.但是要使python进程间共享数据,我们就要使用multiprocessing.Manager. Manager()返回的 ...

  6. 分享 koa + mysql 的开发流程,构建 node server端,一次搭建个人博客

    前言 由于一直在用 vue 写业务,为了熟悉下 react 开发模式,所以选择了 react.数据库一开始用的是 mongodb,后来换成 mysql 了,一套下来感觉 mysql 也挺好上手的.re ...

  7. HTML5笔记-加强版

    新增的语法结构表单验证   1.新的页面结构以及宽松的语法规范:<!doctype html> <meta charset=“utf-8”/> 2.新的结构化元素:语义化标签: ...

  8. CF821 B. Okabe and Banana Trees 简单数学

    Link 题意:给出一条直线,在直线上取一点,其垂直x,y轴作成一个,求矩阵中所有包含的点的x,y坐标之和的最大值. 思路:对于一个任意一点我们计算公式,对于任意一点$(x, y)$,有$(x+y)^ ...

  9. 816B. Karen and Coffee 前缀和思维 或 线段树

    LINK 题意:给出n个[l,r],q个询问a,b,问被包含于[a,b]且这样的区间数大于k个的方案数有多少 思路:预处理所有的区间,对于一个区间我们标记其(左边界)++,(右边界+1)--这样就能通 ...

  10. Disruptor的使用

    ..................2015年的第一天................... 本文代码托管在 https://github.com/hupengcool/disruptor-start ...