原文链接:http://blog.csdn.net/olansefengye1/article/details/53291074

一、事件(Event)原理解析

1、线程同步Event,主要用于线程间的等待通知。 
2、内核对象中,事件内核对象是个最基本的对象。 
3、事件包含一个使用计数(与所有内核对象一样),一个用于指明该事件是个自动重置的事件还是人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。 
4、事件能够通知一个操作已经完成。 
5、有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。 
6、当一个线程执行初始化操作,然后通知另一个线程执行剩余的操作时,事件使用得最多。 
7、事件初始化为未通知状态,然后,当该线程完成它的初始化操作后,它就将事件设置为已通知状态。这时,一直在等待该事件的另一个线程发现该事件已经得到通知,因此它就变成可调度线程。

二、Win32平台源码

1、相关头文件及API

函数名 函数说明
CreateEvent Creates or opens a named or unnamed event object.
CreateEventEx Creates or opens a named or unnamed event object and returns a handle to the object.
OpenEvent Opens an existing named event object.
PulseEvent Sets the specified event object to the signaled state and then resets it to the nonsignaled state after releasing the appropriate number of waiting threads.
ResetEvent Sets the specified event object to the nonsignaled state.
SetEvent Sets the specified event object to the signaled state.
/*头文件*/
#include<windows.h>
HANDLE  CreateEvent(
LPSECURITY_ATTRIBUTESlpEventAttributes, // 安全属性
BOOLbManualReset, // 复位方式
BOOLbInitialState, // 初始状态
LPCTSTRlpName // 对象名称
);

参数: 
lpEventAttributes[In]: 一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。Windows NT/2000:lpEventAttributes的结构中的成员为新的事件指定了一个安全符。如果lpEventAttributes是NULL,事件将获得一个默认的安全符。 
bManualReset[In]: 指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当事件被一个等待线程释放以后,系统将会自动将事件状态复原为无信号状态。 
bInitialState[In]: 指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。 
lpName[In]:指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。 如果lpName指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候,由于bManualReset和bInitialState参数已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes是参数不是NULL,它将确定此句柄是否可以被继承,但是其安全描述符成员将被忽略。 如果lpName为NULL,将创建一个无名的事件对象。 如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。 
返回值:如果函数调用成功,函数返回事件对象的句柄。如果对于命名的对象,在函数调用前已经被创建,函数将返回存在的事件对象的句柄,而且在GetLastError函数中返回ERROR_ALREADY_EXISTS。如果函数失败,函数返回值为NULL,如果需要获得详细的错误信息,需要调用GetLastError。

HANDLE WINAPI CreateEventEx(
__in_opt LPSECURITY_ATTRIBUTES lpEventAttributes,
__in_opt LPCTSTR lpName,
__in DWORD dwFlags,
__in DWORD dwDesiredAccess
);

参数: 
lpEventAttributes[in, optional] :一个指向SECURITY_ATTRIBUTES结构的指针,如果该参数设为NULL,那么事件内核对象的句柄不能被子进程继承. 
lpName[in, optional] :指向事件内核对象的名称字符串的指针,如果该参数设为NULL,那么这个对象被创建为一个匿名事件内核对象. 
dwFlags[in] :这个参数可被设为以下一个或多个值:CREATE_ EVENT_ INITIAL_ SET 0x00000002 表示对象初始状态为已触发,否则为未触发;CREATE_ EVENT_ MANUAL_RESET 0x00000001 表示这个事件对象必须用ResetEvents函数手动重置,如果不设置这个标志,系统会在内核对象被释放后自动重置. 
dwDesiredAccess[in] :访问权限描述标记。 
返回值:如果函数调用成功,返值是所创建或打开的事件内核对象的句柄.如果调用失败则返回NULL。

HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);

参数说明: 
dwDesiredAccess [in]:指定对事件对象的请求访问权限,如果安全描述符指定的对象不允许要求通过对调用该函数的过程,函数将返回失败。该参数必须设置为以下值:EVENT_ALL_ACCESS 指定事件对象所有可能的权限。 
bInheritHandle [in]:指定是否返回的句柄是否继承 。该参数必须设置为false。 
lpName[in]:指向一个以null结束的字符串,即将要打开的事件对象的名字。名称是区分大小写的。 
返回值:函数执行成功则返回事件对象的句柄;失败则返回NULL,获取错误信息可以使用GetLastError。

DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle,
__in DWORD dwMilliseconds
);

参数: 
hHandle[in]:对象句柄。可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。 
dwMilliseconds[in]:定时时间间隔,单位为milliseconds(毫秒).如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。

DWORD WaitForMultipleObjects(
DWORD nCount, // number of handles in the handle array
CONST HANDLE *lpHandles, // pointer to the object-handle array
BOOL fWaitAll, // wait flag
DWORD dwMilliseconds // time-out interval in milliseconds
);

参数: 
nCount: 句柄的数量 最大值为MAXIMUM_WAIT_OBJECTS(64) 
HANDLE: 句柄数组的指针。 HANDLE 类型可以为(Event,Mutex,Process,Thread,Semaphore )数组 。 
BOOL bWaitAll: 等待的类型,如果为TRUE 则等待所有信号量有效在往下执行,FALSE 当有其中一个信号量有效时就向下执行。 
DWORD dwMilliseconds: 超时时间 超时后向执行。 如果为WSA_INFINITE 永不超时。如果没有信号量就会在这死等。

另外: 
一个Event被创建以后,可以用OpenEvent()**API来获得它的Handle,用**CloseHandle() 来关闭它,用SetEvent()PulseEvent()来设置它使其有信号,用ResetEvent() 来使其无信号,用WaitForSingleObject()WaitForMultipleObjects()来等待 其变为有信号。 
PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event 对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的. 对自动复位的Event对象,它仅释放第一个等到该事件的thread(如果有),而对于 人工复位的Event对象,它释放所有等待的thread.

3、Win32源码

/************************MyEvent.h******************************/
#ifndef _MY_EVENT_H
#define _MY_EVENT_H #include <windows.h> class CMyEvent
{
public:
CMyEvent()
{
m_hEvent = CreateEvent(NULL /*安全属性指针*/
, false /*复位方式*/
, true /*初始化状态*/
, NULL /*事件名称*/
);
if(NULL == m_hEvent)
{
return;
}
} ~CMyEvent()
{
CloseHandle(m_hEvent);
} void Lock()
{
WaitForSingleObject(m_hEvent,INFINITE);
} void UnLock()
{
SetEvent(m_hEvent);
} private:
HANDLE m_hEvent;
}; class CEventAutoLock
{
public:
CEventAutoLock(CMyEvent* pMyEvent)
: m_pMyEvent(pMyEvent)
{
if(NULL != m_pMyEvent)
{
m_pMyEvent->Lock();
}
} ~CEventAutoLock()
{
m_pMyEvent->UnLock();
} private:
CMyEvent *m_pMyEvent;
}; #endif
/****************************main.cpp****************************/
#include <iostream>
#include <windows.h>
#include "MySemaphore.h"
#include "MyMutex.h"
#include "MyCriticalSection.h"
#include "MyEvent.h"
using namespace std; CMySemaphore g_MySemaphore; //信号量
CMyMutex g_MyMutex; //互斥量
CMyCriticalSection g_MyCriticalSection; //临界区
CMyEvent g_MyEvent; //事件 DWORD WINAPI Fun(LPVOID lpParamter)
{
string strPrint((const char*)lpParamter);
int iRunTime = 0;
//执行100次跳出
while(++iRunTime<10)
{
{
CEventAutoLock clock(&g_MyEvent);
cout <<"["<< iRunTime <<"]:"<< strPrint.c_str()<<endl;
}
}
return 0;
} int main()
{
//创建五个子线程
string str1 = "A";
string str2 = "B";
string str3 = "C";
string str4 = "D";
string str5 = "E"; HANDLE hThread1 = CreateThread(NULL, 0, Fun, (void*)str1.c_str(), 0, NULL);
HANDLE hThread2 = CreateThread(NULL, 0, Fun, (void*)str2.c_str(), 0, NULL);
HANDLE hThread3 = CreateThread(NULL, 0, Fun, (void*)str3.c_str(), 0, NULL);
HANDLE hThread4 = CreateThread(NULL, 0, Fun, (void*)str4.c_str(), 0, NULL);
HANDLE hThread5 = CreateThread(NULL, 0, Fun, (void*)str5.c_str(), 0, NULL); //关闭线程
CloseHandle(hThread1);
CloseHandle(hThread2);
CloseHandle(hThread3);
CloseHandle(hThread4);
CloseHandle(hThread5); getchar();
// system("pause");
return 0;
}

执行结果: 

Linux平台

在Linux平台下没有专门的Event(事件)对象,可以依靠Mutex实现和Event相同的功能。Mutex的使用可参见我前面的博客。

C++多线程同步之事件(Event)的更多相关文章

  1. 并发编程~~~多线程~~~线程queue, 事件event,

    一 线程queue 多线程抢占资源,只能让其串行. 互斥锁 队列 import queue q = queue.Queue() # 先进先出 q = queue.LifoQueue() # 先进后出 ...

  2. [b0041] python 归纳 (二六)_多进程数据共享和同步_事件Event

    # -*- coding: utf-8 -*- """ 多进程 同步 事件multiprocessing.Event 逻辑: 子线程负责打印,会阻塞, 等待主进程发出控制 ...

  3. 多线程(五)多线程同步_Event事件

    事件和互斥体同样属于内核同步对象,它和互斥体以及临界区在功能上有以下区别 前面的互斥体和临界区主要作用在于确保控制多个线程之间对共享资源访问,保证共享资源的完整性 事件主要作用是通知其它线程一个操作己 ...

  4. MFC线程(三):线程同步事件(event)与互斥(mutex)

    前面讲了临界区可以用来达到线程同步.而事件(event)与互斥(mutex)也同样可以做到. Win32 API中的线程事件 HANDLE hEvent = NULL; void MainTestFu ...

  5. [一个经典的多线程同步问题]解决方案二:Event事件

    使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权”特性所以关键段只能用于线程的互斥而不能用于同步.本篇介绍用事件Event来尝试解决这个线程同步问题. 首先介绍下如何使用事件.事件E ...

  6. 多线程面试题系列(6):经典线程同步 事件Event

    上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的"线程所有权"特性所以关键段只能用于线程的互斥而不能用于同步.本篇介绍用事件Event来尝试解决这个线程同步问题.首先 ...

  7. 秒杀多线程第六篇 经典线程同步 事件Event

    原文地址:http://blog.csdn.net/morewindows/article/details/7445233 上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权” ...

  8. 转--- 秒杀多线程第六篇 经典线程同步 事件Event

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...

  9. C#多线程同步事件及等待句柄AutoResetEvent 和 ManualResetEvent

    最近捣鼓了一下多线程的同步问题,发现其实C#关于多线程同步事件处理还是很灵活,这里主要写一下,自己测试的一些代码,涉及到了AutoResetEvent 和 ManualResetEvent,当然还有也 ...

随机推荐

  1. 解决Ubuntu下在firefox中打开Microsoft Outlook Web Access中文乱码

    Edit---Preference--Content--Languages--Choose...---Select a langue to add... 添加中文

  2. Python+Requests接口测试教程(2):requests

    开讲前,告诉大家requests有他自己的官方文档:http://cn.python-requests.org/zh_CN/latest/ 2.1 发get请求 前言requests模块,也就是老污龟 ...

  3. 安卓手机文件管理器简单横向评比 - imsoft.cnblogs

      X-plore文件管理器 个人评价:安卓手机上管理文件的神器,所有文件一览无余,加上自己对软件常用功能的配置,管理文件无比方便.(本人一直使用)   Solid文件管理器 个人评价:用户体验真的很 ...

  4. Spring Boot(5) 集成Hibernate 日志配置

    https://blog.csdn.net/ZNG_XP/article/details/78131809 https://blog.csdn.net/u011998835/article/detai ...

  5. pandas Timestamp的用法

    (Timestamp('2018-08-01 00:00:00'), <class 'pandas._libs.tslibs.timestamps.Timestamp'>) 注意这里面的T ...

  6. 将一个list转成json数组-晚上坐49路回去打卡

  7. 通过直接编码添加折线图到ChartControl

    https://documentation.devexpress.com/#WindowsForms/CustomDocument2976 ChartControl lineChart = new C ...

  8. nyoj 单调递增子序列(二)

    单调递增子序列(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:4   描述 给定一整型数列{a1,a2...,an}(0<n<=100000),找出单调递增最长 ...

  9. websocket小体验

    http://www.cnblogs.com/GoodHelper/p/7078381.html https://segmentfault.com/a/1190000012084213

  10. hadoop项目开发案例方案汇总

    大数据Hadoop应用开发技术正可谓如火如荼推进中,以为大数据已经不仅仅是局限在互联网领域,而是已经被上升到了国家战略的高度层面.大数据正在深刻影响和改变我们的日常生活和工作方式. Hadoop应用开 ...