VC 线程同步(事件)

2018-02-22  本文已影响27人  YuWenHaiBo

1.创建事件

HANDLE CreateEvent( //创建事件
LPSECURITY_ATTRIBUTES lpEventAttributes,// 安全属性
BOOL bManualReset,// 复位方式
BOOL bInitialState,// 初始状态
LPCTSTR lpName // 对象名称
);
返回值:
    如果函数调用成功,函数返回事件对象的句柄。如果对于命名的对象,在函数调用前已经被创建,函数将返回存在的事件对象的句柄,而且在GetLastError函数中返回ERROR_ALREADY_EXISTS。
如果函数失败,函数返回值为NULL,如果需要获得详细的错误信息,需要调用GetLastError。

参数说明:
lpEventAttributes[输入]
    一个指向[SECURITY_ATTRIBUTES]结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。
    Windows NT/2000:lpEventAttributes的结构中的成员为新的事件指定了一个安全符。如果lpEventAttributes是NULL,事件将获得一个默认的安全符。
 
bManualReset[输入]         
      创建的Event是自动复位还是人工复位.如果true,人工复位,   一旦该Event被设置为有信号,则它一直会等到ResetEvent()API被调用时才会恢复为无信号.   
      如果为false,Event被设置为有信号,则当有一个wait到它的Thread时,  该Event就会自动复位,变成无信号.  
      如果想在每次调用WaitForSingleObject 后让WINDOWS为您自动地把事件地状态恢复为”无信号”状态,必须把该参数设为FALSE,否则,您必须每次调用ResetEvent函数来清除事件的信号。
bInitialState[输入]           
      初始状态,true,有信号,false无信号   
lpName[输入]               
    指定事件的对象的名称,是一个以0 结束的字符串[指针]。
    名称的[字符格式]限定在MAX_PATH之内。名字是对大小写敏感的。
    如果lpName指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候,由于bManualReset和bInitialState参数已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes是参数不是NULL,它将确定此句柄是否可以被继承,但是其[安全描述符]成员将被忽略。
    如果lpName为NULL,将创建一个无名的事件对象。
    如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。

2.打开事件

HANDLE WINAPI OpenEvent(
  _In_ DWORD dwDesiredAccess,
  _In_ BOOL bInheritHandle,
  _In_ LPCTSTR lpName
);
参数:
dwDesiredAccess [in]
  对事件对象的访问。如果指定对象的安全描述符不允许请求访问调用进程,则该函数将失败。
  有关访问权限的列表,请参阅 [同步对象安全和访问权限](https://msdn.microsoft.com/en-us/library/windows/desktop/ms686670(v=vs.85).aspx)。

bInheritHandle [in]
如果此值为TRUE,则由此进程创建的进程将继承该句柄。否则,进程不会继承此句柄。

lpName [in]
参考上面的lpName
## 返回值
如果函数成功,则返回值是事件对象的句柄。
如果函数失败,返回值为NULL。要获得更多的错误信息,请调用 [GetLastError](https://msdn.microsoft.com/en-us/library/windows/desktop/ms679360(v=vs.85).aspx)。

3.重置和触发事件

BOOL ResetEvent(HANDLE hEvent);  //事件对象设置为无信号状态。
BOOL SetEvent(HANDLE hEvent);  //事件对象设置为有信号状态。 
BOOL PulseEvent(HANDLE hEvent);  //事件对象设置为有信号状态,脉冲一个事件
这里的hEvent就是上面创建成功的返回值,这里不多介绍了,主要说说SetEvent和PulseEvent区别
PulseEvent与SetEvent在事件对象为自动模式时是没分别的。 
在人工模式下,区别应该是: 
事件对象调用SetEvent()后,所有等待线程释放,事件对象如没有调用ResetEvent(),将一直处于有信号状态,下一个时间片出现的等待线程直接导致被释放; 
事件对象调用PulseEvent()后,所有等待线程释放,事件对象重置为无信号状态,下一个时间片出现的等待线程将等待事件对象设置为有信号状态才释放。

4.等待内核对象函数

DWORD WINAPI WaitForSingleObject(
  _In_ HANDLE hHandle,
  _In_ DWORD  dwMilliseconds
);
WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,
但如果参数 dwMilliseconds 为 INFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码。
参数 :
hHandle 
是一个事件的句柄,
dwMilliseconds 
是时间间隔。
如果时间是有信号状态返回 WAIT_OBJECT_0 ,如果时间超过 dwMilliseconds 值但时间事件还是无信号状态则返回 WAIT_TIMEOUT 。
hHandle 可以是下列对象的句柄:
Change notification 
Console input 
Event 
Job 
Memory resource notification 
Mutex 
Process 
Semaphore 
Thread 
Waitable timer  
返回值:
如果此函数成功,该函数的返回之标识了引起该函数返回的事件。返回值如下:
  WAIT_ABANDONED(0x00000080L)
  指定的对象是一个互斥对象,该对象没有被拥有该对象的线程在线程结束前释放。互斥对象的所有权被同意授予调用该函数的线程。互斥对象被设置成为无信号状态。
  WAIT_OBJECT_0 (0x00000000L)
  指定的对象出有有信号状态。
  WAIT_TIMEOUT (0x00000102L)
  超过等待时间,指定的对象处于无信号状态
如果失败,返回 WAIT_FAILED;

5.下面一个例子来展示下怎么用事件来实现线程同步

#include <stdio.h>
#include <Windows.h>
#include <thread>
HANDLE g_hEvent;

UINT thread1()
{
    printf("进入线程了,等待5秒\n");
    WaitForSingleObject(g_hEvent, INFINITE);
    printf("等待结束了,向下执行了!\n");
    return 0;
}

UINT thread2()
{
    Sleep(5000);
    SetEvent(g_hEvent);
    return 0;
}

int main(int argc, char*argv[])
{
    g_hEvent = CreateEvent(NULL, true, FALSE, NULL);
    ResetEvent(g_hEvent);

    std::thread t1(thread1 );//开启线程1
    std::thread t2(thread2 );//开启线程2
    t1.join();
    system("pause");
    return 0;
}

通过以上学习可以发现,代码中的一个参数可能对应着很多的知识,这里一个例子并不能完全的把所有的参数演示完毕,在以后的项目中大家可以更多的去体会到其他的参数的作用的。

上一篇 下一篇

猜你喜欢

热点阅读