1、说明
事件的同步是一个很麻烦的区域,虽然有很多方法去处理它。市面上也有很多书和文章介绍怎样避免多线程产生的噩梦。通过处理这些噩梦,我渐渐理解了WIN32的事件(Win32 Kernel objects)。在最初时,我不能理解怎么通过在线程中使用全局变量来使用全局的核心事件;后来我理解后,我发现这个是很容易来使用的。这里,我将解释Win32事件中关于自动和手动设置重置事件的内容。
2、关于Win32 Events
Win32 Events也像其他的核心对象一样在跨进程是可利用的。一个Win32事件就是一个状态机,它的生命期间基于2种状态--Signaled state(激活状态,有信号状态) and Non Signaled state(为激活状态,无信号状态)。 一个事件处于激活状态,意味着这个事件可以停止正在等待这个激活信号的线程;而一个处于非激活状态的,意味着它不能停止这个正在等待这个事件信号的线程。
3、自动重置Win32事件
自动重置事件----它保证关闭单线程(这个单线程正处于等待这个事件的发生,然后返回到非激活状态);如果有多个线程正在同时等待这个事件信号,这些线程是随机触发关闭的。
一些Win32事件创建和使用的API:
CreateEvent(); //创建事件的API
CreateThread(); //创建线程的API
//等待一个事件(信号)的API
WaitForSingleObject();
WaitForMultipleObject();
OpenHandle(); //获得一个事件句柄的API
SetEvent(); //使一个事件处于激活状态的API
ResetEvent(); //使一个事件处于非激活状态的API
CloseHandle(); //关闭事件句柄的API
下面是自动重置事件的相关代码(VS2010):
#include <Windows.h>
#include <iostream>
using namespace std;
// 需要激活的线程
DWORD WINAPI ThreadFun(LPVOID n)
{
cout << "Thread Instantiated" << endl;
// 获得激活事件的句柄(以"MyEvent"为标识)
HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, false, (LPCWSTR)"MyEvent"); // (LPCWSTR)在vs2010,不加入的话会报错
if (!hEvent)
{
return -1;
}
// 循环2次
for (int count = 0; count < 2; ++count)
{
// 等待,直到hEvent事件被激活
WaitForSingleObject(hEvent, INFINITE);
cout << "Got The Signal.." << endl;
}
// 关闭事件的句柄
CloseHandle(hEvent);
cout << "End The Thread" << endl;
return 0;
}
int main()
{
// 创建一个自动重置事件("MyEvent"为它的标识):当它被激活后将自动重置为未激活状态
HANDLE hEvent = CreateEvent(NULL, false, false, (LPCWSTR)"MyEvent");
if (!hEvent)
{
return -1;
}
// 创建一个线程,这个线程执行ThreadFun()函数
DWORD Id;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFun, 0, 0, &Id); // LPTHREAD_START_ROUTINE:指向一个函数,该函数通知宿主某个线程已开始执行。(MSDN)
if (!hThread)
{
CloseHandle(hEvent);
return -1;
}
// 在开始前等待1秒。。。。。
Sleep(1000);
// 循环2次(事件被激活2次)
for (int count = 0; count < 2; ++count)
{
// 激活hEvent事件
SetEvent(hEvent);
// 在发送第二次激活状态前等待2秒
Sleep(2000);
}
// 等待hThread这个线程的结束
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hEvent);
CloseHandle(hThread);
cout << "" << endl;
return 0;
}
// 附加
/****定义全局的
CreateEvent(NULL, false, false, (LPCWSTR)"Global\\MyEvent");
“Global\\xxxEvent ”可以保证:在创建命名时间对象时指定名字是全局的。
这样做的好处如下:
这样创建的内核对象无论出于服务,还是内核中,应用层都可以打开并使用这个内核对象。
CreateEvent( NULL, FALSE, FALSE, "Global\\CSAPP" ); 这是一个内核对象。
*/
说明:上面的代码通过调用“CreateEvent ( NULL , false , false , "MyEvent" ); ”来创建一个事件,他的参数说明如下(网络搜索到的):
有四个参数,分别注解
LPSECURITY_ATTRIBUTES pEventAttributes :安区参数 一般用户不用考虑它
BOOL bManualReset:表示SetEvent 置位,WaitForSingleObject使用以此后;信号量的有无,为TRUE 的时候,表示有信号,为FALSE为无信号,也可以理解为SetEvent 的作用是置一次信号,与PulseEvent函数作用相同了。(我的理解:就是是否能过自动重置为未激活状态。false时意味是可以自动重置的,true表示必须手动,即调用ResetEvent)
BOOL bInitialState:表示初始时的信号量有无,为TRUE 的时,表示有信号,反之无信号
LPCTSTR lpName:信号量的别名
其中主要以BOOL bManualReset,BOOL bInitialState的使用最为重要,采用那种配对方式取决于程序的工作方式了。
4、手动重置Win32事件
需要手动来重置,只要在创建事件时把第二个参数由false改为true即可。这意味着在将事件激活后,如果需要使事件为非激活的状态,必须手动(explicit明确)的调用API(ResetEvent)。下面的代码和上面的很相似,只在main中重置事件为非激活状态时,加入了一些代码:
#include <Windows.h>
#include <iostream>
using namespace std;
// 需要激活的线程
DWORD WINAPI ThreadFun(LPVOID n)
{
cout << "Thread Instantiated" << endl;
// 获得激活事件的句柄(以"MyEvent"为标识)
HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, false, (LPCWSTR)"MyEvent"); // (LPCWSTR)在vs2010,不加入的话会报错
if (!hEvent)
{
return -1;
}
// 循环2次
for (int count = 0; count < 2; ++count)
{
// 等待,直到hEvent事件被激活
WaitForSingleObject(hEvent, INFINITE);
cout << "Got The Signal.." << endl;
// 使hEvent该事件的状态为非激活状态
ResetEvent(hEvent);
cout << "Reset The Event No Signal.." << endl;
}
// 关闭事件的句柄
CloseHandle(hEvent);
cout << "End The Thread" << endl;
return 0;
}
int main()
{
// 创建一个自动重置事件("MyEvent"为它的标识):当它被激活后将自动重置为未激活状态
HANDLE hEvent = CreateEvent(NULL, false, false, (LPCWSTR)"MyEvent");
if (!hEvent)
{
return -1;
}
// 创建一个线程,这个线程执行ThreadFun()函数
DWORD Id;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFun, 0, 0, &Id); // LPTHREAD_START_ROUTINE:指向一个函数,该函数通知宿主某个线程已开始执行。(MSDN)
if (!hThread)
{
CloseHandle(hEvent);
return -1;
}
// 在开始前等待1秒。。。。。
Sleep(1000);
// 循环2次(事件被激活2次)
for (int count = 0; count < 2; ++count)
{
// 激活hEvent事件
SetEvent(hEvent);
// 在发送第二次激活状态前等待2秒
Sleep(2000);
}
// 等待hThread这个线程的结束
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hEvent);
CloseHandle(hThread);
cout << "" << endl;
return 0;
}
这篇文件主要讲在多线程中控制线程的激活、等待、退出等处理形式。
原文地址:
http://www.codeproject.com/KB/winsdk/Win32_Event_Handling.aspx 源码下载:
原作者代码:
/Files/tiger7/Win32_Event_Handling.zip VS2010代码:
/Files/tiger7/Thread_Sync.rar