线程同步之事件
Posted _拥你一生,如沐星辰_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程同步之事件相关的知识,希望对你有一定的参考价值。
事件:事件Event实际上是个内核对。事件类似于前面的信号量,一个事件有两种状态:激发状态和未激发状态。也称有信号状态和无信号状态。
事件又分两种类型:手动重置事件和自动重置事件。手动重置事件被设置为激发状态后,会唤醒所有等待的线程,而且一直保持为激发状态,直到程序重新把它设置为未激发状态。自动重置事件被设置为激发状态后,会唤醒“一个”等待中的线程,然后自动恢复为未激发状态。
自动Event可以被抽象为四个操作:
- 创建 CreateEvent(NULL,false,true,NULL);
- 带触发 WaitForSingleObject(g_hEvent, INFINITE);
- 重置激活 SetEvent(g_hEvent);
- 销毁 CloseHandle(g_hEvent);
手动Event可以被抽象为五个操作:
- 创建 CreateEvent(NULL,true,true,NULL);
- 带触发 WaitForSingleObject(g_hEvent, INFINITE);
- 重置未激活 ResetEvent(g_hEvent);
- 重置激活 SetEvent(g_hEvent);
- 销毁 CloseHandle(g_hEvent);
函数解析:
1.
函数功能描述:创建或打开一个命名的或无名的事件对象
函数原型:
HANDLE CreateEvent
(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性
BOOL bManualReset, // 复位方式
BOOL bInitialState, // 初始状态
LPCTSTR lpName // 对象名称
);
参数:
lpEventAttributes:
一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。如果
lpEventAttributes是NULL,事件将获得一个默认的安全符。
bManualReset:
指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当事件被一个等待线程释放以后,系统将会自动将事件状态复原为无信号状态。
bInitialState:
指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
lpName:
指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。
注意:
1.如果lpName指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候,由于bManualReset和bInitialState参数已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes是参数不是NULL,它将确定此句柄是否可以被继承,但是其安全描述符成员将被忽略。
2.如果lpName为NULL,将创建一个无名的事件对象。
3.如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。
注意点:
手动与自动的区别:
自动重置: SetEvent之后, 事件自动重置为未触发状态。
手动重置: SetEvent之后, 需要调用ResetEvent事件才置为未触发状态。
区别: 当一个手动重置事件被触发的时候, 正在等待该事件的所有线程都变为可调度状态; 当一个自动重置事件被触发的时候,
只有一个正在等待该事件的线程会变为可调度状态. 系统并不会保证会调度其中的哪个线程, 剩下的线程将继续等待. 这样, 可以在在每个线程函数返回之前调用SetEvent
换句话说:
(1)对于手动置位事件,所有正处于等待状态下线程都变成可调度状态。
(2)对于自动置位事件,所有正处于等待状态下线程只有一个变成可调度状态。
2.
函数原型:
DWORD WaitForSingleObject
(
HANDLE hHandle,
DWORD dwMilliseconds
);
参数解析:
参数hHandle是一个事件的句柄,第二个参数dwMilliseconds是时间间隔(INFINITE 永久)。如果时间是有信号状态返回。
WaitForSingleObject函数用来检测hHandle事件的信号状态。如果dwMilliseconds为有限事件,则当函数的执行时间超过dwMilliseconds就返回,
但如果参数dwMilliseconds为INFINITE时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到WaitForSingleObject有返回直才执行后面的代码。
注意:
(1)如果是自动置位事件,那么每一次WaitForSingleObject后,此时状态就会变成未激发状态,就要用SetEvent()进行激活。
(2)如果是手动置位事件,每一次WaitForSingleObject后,不会改变原有的状态。要利用ResetEvent()设置为未激活状态,再利用SetEvent()进行激活。
3.
SetEvent
函数功能:触发事件
函数原型:BOOL SetEvent(HANDLEhEvent);
函数说明:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。
4.
ResetEvent
函数功能:将事件设为末触发
函数原型:BOOL ResetEvent(HANDLEhEvent);
源代码: // Semaphore.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <Windows.h> #include <process.h> //线程数 #define g_nThreadNum 5 //信号量 HANDLE g_hEvent; //累加数 int g_nCount = 0; unsigned _stdcall ThreadFunc(void * lParam) { for (int i = 0; i < 100000; ++i) { //等待事件被触发 ,触发后变成无信号 WaitForSingleObject(g_hEvent, INFINITE); g_nCount++; //重置事件为激活 SetEvent(g_hEvent); } return 0; } int _tmain(int argc, _TCHAR* argv[]) { //创建事件 g_hEvent=(HANDLE)CreateEvent(NULL,false,true,NULL); //启动线程 HANDLE pThread[g_nThreadNum]; for (int i = 0; i < g_nThreadNum; ++i) { pThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, 0); if (pThread[i] == 0) { continue; i--; } } //等待线程结束 WaitForMultipleObjects(g_nThreadNum, pThread, TRUE, INFINITE); printf("g_nCount:%d\n", g_nCount); //释放资源 for (int i = 0; i < g_nThreadNum; ++i) { CloseHandle(pThread[i]); } CloseHandle(g_hEvent); getchar(); return 0; }
以上是关于线程同步之事件的主要内容,如果未能解决你的问题,请参考以下文章
win32下多线程同步方式之临界区,互斥量,事件对象,信号量