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. 解题:POI 2015 Pieczęć

    题面 发现好像没有什么好做法,那就模拟么=.= 以印章左上角的'x'为基准,记录印章上'x'的相对位置模拟.记录相对位置是因为可能有这种情况↓ 直接模拟是会漏掉的=.= #include<cst ...

  2. day2-python基础

  3. Spring MVC @ModelAttribute详解

    被@ModelAttribute注释的方法会在此controller每个方法执行前被执行,因此对于一个controller映射多个URL的用法来说,要谨慎使用. 我们编写控制器代码时,会将保存方法独立 ...

  4. Qt ------ QProcess,启动外部进程,进程间通信

    简介: 可用于完成启动外部程序,并与之交互通信. 启动一个进程的名字叫“program”,如果某进程的路径没有设置成环境变量,“program”需要包含路径 如果进程可以接收参数,参数叫“argume ...

  5. JS中的new操作符原理解析

    var Person = function(name){ this.name = name; } Person.prototype.sayHello = function() { console.lo ...

  6. 新Linux系统配置yum源

    新的Linux系统安装好以后,yum的源还是需要配置一下的,我使用的是redhat6.6版本,同时为了不注册而使用更多的yum源的资源,也需要做一下的修改. 1. 删除redhat原有的yum源 # ...

  7. Grass is Green

    Root    3719 - Grass is Green Time limit: 3.000 seconds This year exactly n <tex2html_verbatim_ma ...

  8. (32位)本体学习程序(ontoEnrich)系统配置说明文档

    1.系统环境 32位 Ubuntu 源代码中已经包含在32位下编译生成的.o文件,配置好依赖库(步骤2)后,参考步骤3则可重新link. link无误即可运行程序. 2.依赖库  2.1 boost_ ...

  9. C#为何不推荐在构造函数中访问虚成员

    如果在一个类中定义了虚属性或者虚方法,又在构造函数中访问了这个虚属性或方法,此时VisualStudio是不会给出警告,并且编译也没有问题,但是如果安装了Resharper插件则会给出警告提示:&qu ...

  10. 关于aspx.designer.cs的研究

    .aspx文件..aspx.cs文件和.aspx.designer.cs的一些说明 .aspx文件:(页面)书写页面代码.存储的是页面design代码.只是放各个控件的代码,处理代码一般放在.cs文件 ...