CPP-基础:事件
事件(Event)
事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。
信号量包含的几个操作原语:
CreateEvent() 创建一个事件
OpenEvent() 打开一个事件
SetEvent() 回置事件
WaitForSingleObject() 等待一个事件
WaitForMultipleObjects()等待多个事件
WaitForMultipleObjects 函数原型:
- WaitForMultipleObjects(
- IN DWORD nCount, // 等待句柄数
- IN CONST HANDLE *lpHandles, //指向句柄数组
- IN BOOL bWaitAll, //是否完全等待标志
- IN DWORD dwMilliseconds //等待时间
- )
参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。 dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回 WAIT_TIMEOUT。在描述线程通信时曾使用过事件内核对象来进行线程间的通信,除此之外,事件内核对象也可以通过通知操作的方式来保持线程的同步。对于前面那段使用临界区保持线程同步的代码可用事件对象的线程同步方法改写如下:
- // 事件句柄
- HANDLE hEvent = NULL;
- // 共享资源
- char g_cArray[];
- ……
- UINT ThreadProc12(LPVOID pParam)
- {
- // 等待事件置位
- WaitForSingleObject(hEvent, INFINITE);
- // 对共享资源进行写入操作
- for (int i = ; i < ; i++)
- {
- g_cArray[i] = 'a';
- Sleep();
- }
- // 处理完成后即将事件对象置位
- SetEvent(hEvent);
- return ;
- }
- UINT ThreadProc13(LPVOID pParam)
- {
- // 等待事件置位
- WaitForSingleObject(hEvent, INFINITE);
- // 对共享资源进行写入操作
- for (int i = ; i < ; i++)
- {
- g_cArray[ - i - ] = 'b';
- Sleep();
- }
- // 处理完成后即将事件对象置位
- SetEvent(hEvent);
- return ;
- }
- ……
- void CSample08View::OnEvent()
- {
- // 创建事件
- hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- // 事件置位
- SetEvent(hEvent);
- // 启动线程
- AfxBeginThread(ThreadProc12, NULL);
- AfxBeginThread(ThreadProc13, NULL);
- // 等待计算完毕
- Sleep();
- // 报告计算结果
- CString sResult = CString(g_cArray);
- AfxMessageBox(sResult);
- }
在创建线程前,首先创建一个可以自动复位的事件内核对象hEvent,而线程函数则通过WaitForSingleObject()等待函数无限等待hEvent的置位,只有在事件置位时WaitForSingleObject()才会返回,被保护的代码将得以执行。对于以自动复位方式创建的事件对象,在其置位后一被WaitForSingleObject()等待到就会立即复位,也就是说在执行ThreadProc12()中的受保护代码时,事件对象已经是复位状态的,这时即使有ThreadProc13()对CPU的抢占,也会由于WaitForSingleObject()没有hEvent的置位而不能继续执行,也就没有可能破坏受保护的共享资源。在ThreadProc12()中的处理完成后可以通过SetEvent()对hEvent的置位而允许ThreadProc13()对共享资源g_cArray的处理。这里SetEvent()所起的作用可以看作是对某项特定任务完成的通知。
使用临界区只能同步同一进程中的线程,而使用事件内核对象则可以对进程外的线程进行同步,其前提是得到对此事件对象的访问权。可以通过OpenEvent()函数获取得到,其函数原型为:
- HANDLE OpenEvent(
- DWORD dwDesiredAccess, // 访问标志
- BOOL bInheritHandle, // 继承标志
- LPCTSTR lpName // 指向事件对象名的指针
- );
如果事件对象已创建(在创建事件时需要指定事件名),函数将返回指定事件的句柄。对于那些在创建事件时没有指定事件名的事件内核对象,可以通过使用内核对象的继承性或是调用DuplicateHandle()函数来调用CreateEvent()以获得对指定事件对象的访问权。在获取到访问权后所进行的同步操作与在同一个进程中所进行的线程同步操作是一样的。
如果需要在一个线程中等待多个事件,则用WaitForMultipleObjects()来等待。WaitForMultipleObjects()与WaitForSingleObject()类似,同时监视位于句柄数组中的所有句柄。这些被监视对象的句柄享有平等的优先权,任何一个句柄都不可能比其他句柄具有更高的优先权。WaitForMultipleObjects()的函数原型为:
- DWORD WaitForMultipleObjects(
- DWORD nCount, // 等待句柄数
- CONST HANDLE *lpHandles, // 句柄数组首地址
- BOOL fWaitAll, // 等待标志
- DWORD dwMilliseconds // 等待时间间隔
- );
参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。dwMilliseconds在饫锏淖饔糜朐赪aitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某个值,则说明所有指定对象的状态均为已通知状态(当fWaitAll为TRUE时)或是用以减去WAIT_OBJECT_0而得到发生通知的对象的索引(当fWaitAll为FALSE时)。如果返回值在WAIT_ABANDONED_0与WAIT_ABANDONED_0+nCount-1之间,则表示所有指定对象的状态均为已通知,且其中至少有一个对象是被丢弃的互斥对象(当fWaitAll为TRUE时),或是用以减去WAIT_OBJECT_0表示一个等待正常结束的互斥对象的索引(当fWaitAll为FALSE时)。 下面给出的代码主要展示了对WaitForMultipleObjects()函数的使用。通过对两个事件内核对象的等待来控制线程任务的执行与中途退出:
- // 存放事件句柄的数组
- HANDLE hEvents[];
- UINT ThreadProc14(LPVOID pParam)
- {
- // 等待开启事件
- DWORD dwRet1 = WaitForMultipleObjects(, hEvents, FALSE, INFINITE);
- // 如果开启事件到达则线程开始执行任务
- if (dwRet1 == WAIT_OBJECT_0)
- {
- AfxMessageBox("线程开始工作!");
- while (true)
- {
- for (int i = ; i < ; i++);
- // 在任务处理过程中等待结束事件
- DWORD dwRet2 = WaitForMultipleObjects(, hEvents, FALSE, );
- // 如果结束事件置位则立即终止任务的执行
- if (dwRet2 == WAIT_OBJECT_0 + )
- break;
- }
- }
- AfxMessageBox("线程退出!");
- return ;
- }
- ……
- void CSample08View::OnStartEvent()
- {
- // 创建线程
- for (int i = ; i < ; i++)
- hEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
- // 开启线程
- AfxBeginThread(ThreadProc14, NULL);
- // 设置事件0(开启事件)
- SetEvent(hEvents[]);
- }
- void CSample08View::OnEndevent()
- {
- // 设置事件1(结束事件)
- SetEvent(hEvents[]);
- }
MFC事件
MFC为事件相关处理也提供了一个CEvent类,共包含有除构造函数外的4个成员函数PulseEvent()、ResetEvent()、SetEvent()和UnLock()。在功能上分别相当与Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等函数。而构造函数则履行了原CreateEvent()函数创建事件对象的职责,其函数原型为:
- CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
图2 CEvent类对线程的同步过程示意
B线程在执行到CEvent类成员函数Lock()时将会发生阻塞,而A线程此时则可以在没有B线程干扰的情况下对共享资源进行处理,并在处理完成后通过成员函数SetEvent()向B发出事件,使其被释放,得以对A先前已处理完毕的共享资源进行操作。可见,使用CEvent类对线程的同步方法与通过API函数进行线程同步的处理方法是基本一致的。前面的API处理代码可用CEvent类将其改写为:
- // MFC事件类对象
- CEvent g_clsEvent;
- UINT ThreadProc22(LPVOID pParam)
- {
- // 对共享资源进行写入操作
- for (int i = ; i < ; i++)
- {
- g_cArray[i] = 'a';
- Sleep();
- }
- // 事件置位
- g_clsEvent.SetEvent();
- return ;
- }
- UINT ThreadProc23(LPVOID pParam)
- {
- // 等待事件
- g_clsEvent.Lock();
- // 对共享资源进行写入操作
- for (int i = ; i < ; i++)
- {
- g_cArray[ - i - ] = 'b';
- Sleep();
- }
- return ;
- }
- ……
- void CSample08View::OnEventMfc()
- {
- // 启动线程
- AfxBeginThread(ThreadProc22, NULL);
- AfxBeginThread(ThreadProc23, NULL);
- // 等待计算完毕
- Sleep();
- // 报告计算结果
- CString sResult = CString(g_cArray);
- AfxMessageBox(sResult);
- }
CPP-基础:事件的更多相关文章
- 【深入浅出Linux网络编程】 “基础 -- 事件触发机制”
回顾一下“"开篇 -- 知其然,知其所以然"”中的两段代码,第一段虽然只使用1个线程但却也只能处理一个socket,第二段虽然能处理成百上千个socket但却需要创建同等数量的线程 ...
- CPP基础
CPP基础1. 如果没有指明访问限定符(public,private),class中默认的private,而struct中的成员默认是public的. #include <iostream> ...
- 第一百六十九节,jQuery,基础事件
jQuery,基础事件 学习要点: 1.绑定事件 2.简写事件 3.复合事件 JavaScript 有一个非常重要的功能,就是事件驱动.当页面完全加载后,用户通过鼠标 或键盘触发页面中绑定事件的元素即 ...
- C#基础---事件的使用
一:什么是事件 事件是可以被控件识别的操作,如按下确定按钮,选择某个单选按钮或者复选框.每一种控件有自己可以识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件,等等.事 ...
- 9、网页制作Dreamweaver(jQuery基础:事件)
事件 定义 即当HTML中发生某些事(点击.鼠标移过等)的时候调用的方法 $(selector).action() 触发 事件的触发有两种方法: 1.直接将事件click写在<javascrip ...
- JS基础——事件绑定
上一篇博客JS事件对象中,老师问JS事件处理和VB中的事件处理有什么联系?先来解决一下这个问题.举个VB.net中事件处理的样例(JS敲久了,VB习惯的都不熟悉了,看来得常常回想了): 1.事件处理V ...
- javascript基础-事件1
原理 事件分两种.第一种浏览器事件,由浏览器抛出事件,它是人机交互的基础:第二种自定义事件,由程序员抛出事件,它是模拟事件流程.两者都是为了完成数据的传递. 浏览器事件 机制 冒泡和捕获两种机制.因I ...
- javascript基础-事件2
DOM0,DOM2,DOM3事件类型 图解: 范畴 响应顺序(标:标准浏览器.IE9+) 注意点 MouseEvent 标: mousedown-mouseup-click-mousedown-mou ...
- jQuery事件篇---基础事件
写在前面: 有一段时间未更新博客了,利用这段时间,重新看了<jQuery基础教程 第四版>和<锋利的jQuery 第二版>,这两本书绝对是jQuery入门非常好的书,值得多读几 ...
- C#基础-事件 继承类无法直接引发基类的事件
An event can be raised only from the declaration space in which it is declared. Therefore, a class c ...
随机推荐
- Node.js学习笔记(六) --- Nodejs 的非阻塞 I/O、 异步、 事件驱动
1. Nodejs 的单线程 非阻塞 I/O 事件驱动在 Java. PHP 或者.net 等服务器端语言中,会为每一个客户端连接创建一个新的线程.而每个线程需要耗费大约 2MB 内存.也就是说,理论 ...
- .net iis excel导出问题
碰到几个问题的解决方法 1.当我远程服务器时才可以导出excel!!关闭了远程就不行... 解决:运行mmc -32组件服务 ->DCOM Config->Microsoft Excel ...
- [javaEE] javaweb的mvc设计思想
Servlet:在Servlet中拼接html内容 JSP:在html中拼接java JSP+JavaBean:利用javaBean将大量的代码提取走 Servlet+JSP+JavaBean:Ser ...
- 六、cent OS其它常用命令
进入根目录下的laycloud的目录cd /laycloud 进入当前目录下的目录cd laycloud 查看某个目录下的内容ls /laycloud 查看当前目录下的内容ls 查看当前目录下的内容读 ...
- 《第一行代码》Android特色开发,基于位置服务,出现的问题
手机GPS定位较慢.精度高.耗电量多,网络定位较快.精度低.耗电量少 当位置精度要求非常高的时候,使用GPS定位:一般情况下,使用网络定位. 按<第一行代码>写了一个定位程序,真机一直没有 ...
- java设计模式之装饰者模式学习
装饰者模式 Decorator模式(别名Wrapper):动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案. 装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为 ...
- ubuntu 16.10安装nginx
1 : cd /usr/local 2 : sudo wget http://nginx.org/download/nginx-1.2.8.tar.gz 3 : sudo tar -zxvf ngin ...
- css 元素居中各种办法
一:通过弹性布局<style> #container .box{ width: 80px; height: 80px; position: absolute; background:red ...
- <Android 应用 之路> MPAndroidChart~BarChart
简介 MPAndroidChart是PhilJay大神给Android开发者带来的福利.MPAndroidChart是一个功能强大并且使用灵活的图表开源库,支持Android和IOS两种,这里我们暂时 ...
- sprintf详解
原文:http://www.cnblogs.com/wqlblogger/archive/2007/01/09/615525.html 转摘声明:选自<CSDN 社区电子杂志——C/C++杂志& ...