Windows进程同步之事件内核对象(Event)

在所有的内核对象中,事件内核对象比其他的简单的多,可以用事件内核对象对不同进程进行同步。

事件内核对象主要包括三个部分:使用计数一个表示是自动还是手动重置事件的布尔值一个表示是否有信号的布尔值

使用计数:和其他内核对象一样,用来标识使用该事件对象的不同线程个数。

表示自动或手动重置事件的布尔值:当一个事件是自动重置事件,事件被触发后,只有一个等待的线程会变成可调度状态(根据系统的调度策略),然后该事件会自动变成未触发状态;当一个事件是手动重置事件,事件被触发后,所有等待的线程都会变成可调度状态,该事件在触发后一直为触发状态,直到手动重置该事件为未触发状态。

是否有信号的布尔值:表示改事件是否被触发。

 

下面是使用事件内核对象的所要使用的函数接口:

1CreateEvent()

HANDLE WINAPI CreateEvent(

  _In_opt_  LPSECURITY_ATTRIBUTES lpEventAttributes,

  _In_      BOOL bManualReset,

  _In_      BOOL bInitialState,

  _In_opt_  LPCTSTR lpName

);

lpEventAttributes:事件对象的安全属性,一般置为NULL

bManualReset:事件对象是手动重置事件(TRUE)还是自动重置事件(FALSE);

bInitialState:初始状态时触发状态(TRUE)还是非触发状态(FALSE);

lpName:创建有名的事件对象,用于进程间的共享;

如果该事件对象已经存在,那么CreateEvent会返回该内核对象的句柄,并通过系统返回错误ERROR_ALREADY_EXISTS,通过GetLastError()获得。

2OpenEvent()

HANDLE WINAPI OpenEvent(

  _In_  DWORD dwDesiredAccess,

  _In_  BOOL bInheritHandle,

  _In_  LPCTSTR lpName

);

dwDesiredAccess:指定想要的访问权限,EVENT_ALL_ACCESS 请求对事件对象的完全访问,EVENT_MODIFY_STATE 允许使用 SetEvent,,ResetEventPulseEvent函数;

bInheritHandle:是否希望子进程继承事件对象的句柄,一般设置为false

lpName:要打开的事件对象的名称;

其他进程中的线程可以通过OpenEventCreateEvent访问已经存在的事件内核对象。和其他内核对象的访问一样。

3WaitForSingleObject()

DWORD WINAPI WaitForSingleObject(

  _In_  HANDLE hHandle,

  _In_  DWORD dwMilliseconds

);

hHandle:指向内核对象的句柄;

dwMilliseconds:线程最大等待多长时间,直到该对象被触发。经常使用INFINITE,表示阻塞等待。

WaitForSingleObject被称呼为等待函数,是等待内核对象被触发通用的等待函数,被用在所有的内核对象触发等待中。当事件对象处于未触发状态,等待函数会被阻塞。当处于触发状态时,等待函数会被系统调用,成功返回。当等待函数返回后,该事件对象的状态是被重置为未触发状态还是仍然处于触发状态,由该事件对象是自动重置还是手动重置事件决定。当该事件对象时自动重置事件,等待函数返回时,该事件会变成未触发状态,如果为手动重置事件,那么等待函数返回后,该事件仍然处于触发状态,直到调用ResetEvent函数,使该事件变成未触发状态。

4SetEvent()

BOOL WINAPI SetEvent(

  _In_  HANDLE hEvent

);

hObject:指向内核对象的句柄

设置事件内核对象为触发状态;

5ResetEvent()

BOOL WINAPI ResetEvent(

  _In_  HANDLE hEvent

);

hObject:指向内核对象的句柄

设置事件内核对象为未触发状态;对于事件内核对象,当该事件对象被设置为自动重置事件的时候,ResetEvent的调用时不必要的,因为在自动重置事件上进行等待时,即调用WaitForSingleObject,当等待函数返回时,该事件会被自动重置为未触发的状态。

6CloseHandle()

BOOL WINAPI CloseHandle(

  _In_  HANDLE hObject

);

hObject:指向内核对象的句柄

和其他内核对象一样,无论以什么方式创建内核对象,我们都必须通过调用CloseHandle向系统表明结束使用内核对象。如果传入的句柄有效,系统将获得内核对象数据结构的地址,并将结构中的使用计数减1,如果使用计数0,就会将内核对象销毁,从内存空间中擦除。

 

 

下面是例子:

首先下面是两个进程对头一个文件未经同步的操作:

 

//process 1
int main()
{  
  ofstream fileStream1("c:/test.txt", ios_base::app);
    for (int i = 0, j = 1; i < 10; ++i)
    {
        Sleep(1000);
        fileStream1<<j;
        fileStream1<<' '<<flush;
    }
}
  
  
//process 2
int main()
{
    ofstream fileStream2("c:/test.txt", ios_base::app);
    for (int i = 0, j = 2; i < 10; ++i)
    {
        Sleep(1000);
        fileStream2<<j;
        fileStream2<<' '<<flush;
    }
}

 

结果"c:/test.txt"中的内容如下:

 

2 2 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 1 1 

 

而下面两个进程是通过事件内核对象对文件"c:/test.txt"的操作进行同步,

//process 1
int main()
{  
  //自动触发事件,初始状态为触发
    HANDLE stream1Event = CreateEvent(NULL, false, true, (LPCWSTR)"streamEvent");
    WaitForSingleObject(stream1Event, INFINITE);
   
  ofstream fileStream1("c:/test.txt", ios_base::app);
    for (int i = 0, j = 1; i < 10; ++i)
    {
        Sleep(1000);
        fileStream1<<j;
        fileStream1<<' '<<flush;
    }
    SetEvent(stream1Event);
    CloseHandle(stream1Event);
}
  
  
//process 2
int main()
{
  //自动触发事件,初始状态为触发
    HANDLE stream2Event = CreateEvent(NULL, false, true, (LPCWSTR)"streamEvent");
    WaitForSingleObject(stream2Event, INFINITE);

    ofstream fileStream2("c:/test.txt", ios_base::app);
    for (int i = 0, j = 2; i < 10; ++i)
    {
        Sleep(1000);
        fileStream2<<j;
        fileStream2<<' '<<flush;
    }
    SetEvent(stream2Event);
    CloseHandle(stream2Event);
}

结果"c:/test.txt"中的内容如下:

 

2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 

Jue 14, 2013 @lab

 

 

posted @ 2013-06-14 23:12  爱生活,爱编程  阅读(1005)  评论(0编辑  收藏  举报