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. mysql主从配置的过程

    首先参考MySQL5.5官方手册 以下章节: 6.4节如何设置复制 13.6.1节 用于控制主服务器的SQL语句 13.6.2节 用于控制从服务器的SQL语句 6.8节 复制启动选项 6.5节 不同M ...

  2. 流媒体协议之RTSP客户端的实现20171014

    RtspClient是基于jrtplib实现的,目前仅支持h264格式,后续将不断迭代优化,加入对其他格式的支持,并且将实现RTSP的服务端. RtspClient的功能是接收服务端过来流,然后写入到 ...

  3. mysql 在linux服务器恢复数据表方法记录

    在本地搭建测试环境录入的数据放到线上测试,备份了数据表为一个.sql文件, 在服务器上登录mysql执行 source (如:source exposition_exposition.sql) 文件路 ...

  4. jquery ajax thinkphp异步局部刷新完整流程

    环境:ThinkPHP3.2.3,jQuery3.2   前言: 在一般的网站中,都需要用到jquery或者其他框架(比如angular)来处理前后端数据交互,thinkphp在后台也内置了一些函数用 ...

  5. P4753 River Jumping

    P4753 River Jumping 题目描述 有一条宽度为 NN 的河上,小D位于坐标为 00 的河岸上,他想到达坐标为 NN 的河岸上后再回到坐标为 00 的位置.在到达坐标为 NN 的河岸之前 ...

  6. 前端PHP入门-005-爱情是常量还是变量

    常量 常--汉语字面为:长久,经久不变. 常量那就好翻译了:长久不变的值. 常量的使用范围非常广泛. 我们在以后,定义我们的工作目录.定义一些特点的帐户密码.版本号等我们都会使用到常量.所以这一块的知 ...

  7. [LeetCode] Matrix 值修改系列,例题 Surrounded Regions,Set Matrix Zeroes

    引言 Matrix内部的值修改严格来讲放在一个系列里不大合适,因为对于不同的问题,所用的算法和技巧可能完全不同,权且这样归类,以后需要时再拆分吧. 例题 1 Given a 2D board cont ...

  8. JSOI2008 小店购物

    https://www.luogu.org/problem/show?pid=2792 题目背景 JSOI集训队的队员发现,在他们经常活动的集训地,有一个小店因为其丰富的经营优惠方案深受附近居民的青睐 ...

  9. SQL语句(二十二)—— 权限授予和回收(作业练习)

    CREATE TABLE course ( Cno ) NOT NULL, Cname ) DEFAULT NULL, Cpno ) DEFAULT NULL, Ccredit smallint DE ...

  10. JVM调优总结(6):新一代的垃圾回收算法

    垃圾回收的瓶颈 传统分代垃圾回收方式,已经在一定程度上把垃圾回收给应用带来的负担降到了最小,把应用的吞吐量推到了一个极限.但是他无法解决的一个问题,就是Full GC所带来的应用暂停.在一些对实时性要 ...