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允许我们区分哪些想要读取资源的线程(读取者线程) 和 ...
随机推荐
- 《Java程序设计》第8周学习总结 20165218 2017-2018-1
20165218 2017-2018-1 <Java程序设计>第8周学习总结 教材学习内容总结 第12章 java多线程机制 java中的线程 计算机在任何给定时刻只能执行一个线程,多线程 ...
- R-FCN:基于区域的全卷积网络来检测物体
http://blog.csdn.net/shadow_guo/article/details/51767036 原文标题为“R-FCN: Object Detection via Region-ba ...
- poj 1655 树的重心
Balancing Act Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 13178 Accepted: 5565 De ...
- Codeforces Round #404 (Div. 2)A B C二分
A. Anton and Polyhedrons time limit per test 2 seconds memory limit per test 256 megabytes input sta ...
- Python urllib urlretrieve函数解析
Python urllib urlretrieve函数解析 利用urllib.request.urlretrieve函数下载文件 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考文献 Ur ...
- [LeetCode] 23. Merge k Sorted Lists ☆☆
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 解 ...
- UVA 11982 Fantasy Cricket
https://vjudge.net/problem/UVA-11982 题意: 给出一个包含’U’, ‘D’, ‘E’的字符串, ’U’ 表示需要把这个字符向后移动, ’D’表示需要把这个字符向前移 ...
- POJ 3304 Segments 基础线段交判断
LINK 题意:询问是否存在直线,使得所有线段在其上的投影拥有公共点 思路:如果投影拥有公共区域,那么从投影的公共区域作垂线,显然能够与所有线段相交,那么题目转换为询问是否存在直线与所有线段相交.判断 ...
- 重构改善既有代码设计--重构手法14:Hide Delegate (隐藏委托关系)
客户通过一个委托类来调用另一个对象.在服务类上建立客户所需的所有函数,用以隐藏委托关系. 动机:封装即使不是对象的最关机特性,也是最关机特性之一.“封装”意味着每个对象都应该少了解系统的其他部分.如此 ...
- Git同时push到多个远程仓库
添加第二个远程地址时使用以下命令: git remote set-url --add origin git@github.com:morethink/programming.git 查看远程分支:gi ...