加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

CreateEvent和WaitForSingleObject的用法

(2016-04-18 17:29:16)
标签:

createevent

setevent

waitforsingleobject

分类: 代码天地

CreateEvent和WaitForSingleObject的用法

进程或者线程之间的同步,需要使用到的函数:   一个CreateEvent Event被创建以后,可以用OpenEvent()API来获得它的Handle,用CloseHandle() 来关闭它,用SetEvent()或PulseEvent()来设置它使其有信号。用ResetEvent()来使其无信号。用WaitForSingleObject()或WaitForMultipleObjects()来等待其变为有信号。  


一 设置事件的函数

(1). 原形声明

HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset, BOOL bInitialState,LPCSTR lpName);

 ① lpEventAttributes:确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。 Windows NT/2000:lpEventAttributes的结构中的成员为新的事件指定了一个安全符。如果lpEventAttributes是NULL,事件将获得一个默认的安全符。
 ② bManualReset:指定将事件对象创建成手动复原还是自动复原。TRUE,使用ResetEvent()手动重置为无信号状态;FALSE,当一个等待线程被释放时,自动重置状态为无信号状态。
 ③ bInitialState:指定事件对象的初始状态,当TRUE,初始状态为有信号状态;当FALSE,初始状态为无信号状态。
 ④ lpName:指定事件的对象的名称,名字是对大小写敏感的。如果lpName为NULL,将创建一个无名的事件对象。
      如果lpName指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候,由于 bManualReset和bInitialState已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes是参数不是NULL,它将确定此句柄是否可以被继承,但是其安全描述符成员将被忽略。
      如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。

(2). 返回类型
    如果函数调用成功,函数返回事件对象的句柄。如果对于命名的对象,在函数调用前已经被创建,函数将返回存在的事件对象的句柄,而且在GetLastError函数中返回ERROR_ALREADY_EXISTS。
    如果函数失败,函数返回值为NULL,如果需要获得详细的错误信息,需要调用GetLastError。

(3). 说明

 ① 当事件的对象被置为有信号状态时,任意数量的等待中线程,以及随后开始等待的线程均会被释放。
    当"手动复原"的置为有信号状态时,该对象状态将一直保持有信号状态,直至明确调用ResetEvent函数将其置为无符号状态。
    当"自动复原"的置为有信号状态时,该对象状态将一直保持有信号状态,直至一个等待线程被释放;系统将自动将此函数置为无符号状态。如果没有等待线程正在等待,事件对象的状态将保持有信号状态。
    对自动复位的Event对象,它仅释放第一个等到该事件的thread(如果有),而对于人工复位的Event对象,它释放所有等待的thread 。    

 ② 多个进程可持有同一个事件对象的多个句柄,可以通过使用此对象来实现进程间的同步。
      ·在CreateEvent函数中,lpEventAttributes参数指定句柄可被继承时,通过CreateProcess函数创建的子进程继承的事件对象句柄。
      ·一个进程可以在DuplicateHandle函数中指定事件对象句柄,从而获得一个复制的句柄,此句柄可以被其它进程使用。
      ·一个进程可以在OpenEvent或CreateEvent函数中指定一个名字,从而获得一个有名的事件对象句柄。
      使用CloseHandle函数关闭句柄。当进程停止时,系统将自动关闭句柄。当最后一个句柄被关闭后,事件对象将被销毁。

(4). 与其有关的函数
    一个Event被创建以后,可以用OpenEvent()API来获得它的Handle,用CloseHandle() 来关闭它,用SetEvent()或PulseEvent()来设置它使其有信号。
    用ResetEvent()来使其无信号。用WaitForSingleObject()或WaitForMultipleObjects()来等待其变为有信号。  

    PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event 对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的。  
 
 

 
二 等待事件的函数

1. WaitForSingleObject的用法                                       

(1). 原形声明
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds);

 ① hHandle,是想要等待对对象的句柄,可以是下列句柄:
   Change notification ,Console input ,Event ,Job ,
   Memory resource notification ,Mutex ,Process
   Semaphore ,Thread ,Waitable timer

 ② dwMilliseconds,就是希望等待的时间(毫秒)。0是立即返回,INFINITE(0xFFFFFFFF 或-1)是无限等待。

(2). 返回类型
直到WaitForSingleObject有返回直才执行其后面的代码。

 ① WAIT_OBJECT_0: 表示等待的对象有信号(对线程来说,表示其执行结束)
 ② WAIT_FAILED: 表示对象有信号,但还是不能执行  一般是因为未获取到锁或其他原因
 ③ WAIT_TIMEOUT: 表示等待指定时间内,对象一直没有信号(对线程来说,线程没执行完)


(3).还有一种用法就是我们可以通过WaitForSingleObject函数来间隔的执行一个线程函数的函数体

UINT CMyDlg::Thread2( LPVOID pParam )
{
     while(WaitForSingleObject(g_event,MT_INTERVAL)!=WAIT_OBJECT_0)
     {
         ………………
     }
     return 0;
}

在这个线程函数中可以可以通过设置MT_INTERVAL来控制这个线程的函数体多久执行一次,当事件为无信号状态时函数体隔MT_INTERVAL执行一次,当设置事件为有信号状态时,线程就执行完毕了(return 0)。

(4). 举例

先创建一个全局事件对象:    CEvent g_event;

在程序中可以通过调用CEvent::SetEvent设置事件为有信号状态。

下面是一个线程函数Thread1()

UINT CMyDlg::Thread1( LPVOID pParam )
{
     WaitForSingleObject(g_event,INFINITE);
     while(1)
     {
         …………
     }
     return 0;
}

在线程函数Thread1中,只有g_event为有信号状态,才执行下面的while循环。 全局变量g_event可以在主函数,或者其它线程中g_event.SetEvent来设置。


2. WaitForMultipleObjects的用法

(1). 原形声明
DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);

 ① nCount,用于指定句柄数组的数量
 ② lpHandles,用于指定句柄数组的内存地址
 ③ bWaitAll,如果为TRUE 则等待所有信号量有效才往下执行。FALSE 当有其中一个信号量有效时就向下执行。
 ④ dwMilliseconds,用于指定等待的超时时间(毫秒),可以是INFINITE

(2). 返回类型

 ① WAIT_OBJECT_0到WAIT_OBJECT_0 + nCount - 1
   如果bWaitAll为TRUE,则返回值表明所有指定对象的状态信号
   如果bWaitAll为FALSE,则返回值减去WAIT_OBJECT_0表示等到的有效对象在lpHandles数组中的索引。如果多个对象在此过程中有信号,则返回的只是其中序号最小的那个。

 ② WAIT_ABANDONED_0至WAIT_ABANDONED_0 + nCount - 1
   如果bWaitAll为TRUE,则返回值表明所有对象有了信号,并且其中至少一个是废弃的互斥对象。
   如果bWaitAll为FALSE,则返回值减去WAIT_ABANDONED_0表示等到的废弃对象在lpHandles数组中的索引。   

 ③ WAIT_TIMEOUTThe超时间隔已过,由bWaitAll参数指定的条件得不到满足。


(3). 处理所有同时触发的内核对象。

监测多事件有一个问题,如果序号最小的那个对象频繁被触发,那么序号比它大的内核对象将得不到被处理的机会。为了解决这一问题,可以采用双WaitForMultipleObjects检测机制来实现。
见下面的例子(来自网上程序,增加上了部分注释,便于理解):

DWORD WINAPI ThreadProc(LPVOID lpPara)   
{   
  DWORD dwRet = 0;   
  int nIndex = 0;   
  while(1)   
  { 
//这里最后的参数是INFINITE,是无限等待
    dwRet = WaitForMultipleObjects(nCount,pHandles,false,INFINITE);   
    switch(dwRet)   
    {
      case WAIT_TIMEOUT:   
        break;   
      case WAIT_FAILED:   
        return 1;
      default:
      {
        nIndex = dwRet - WAIT_OBJECT_0;   
        ProcessHanlde(nIndex++);   //自己编写函数,处理有信号的对象
        //同时继续检测其余的事件   
        while(nIndex < nCount) //nCount事件对象总数   
        {   
//注意这里最后的参数是0 ,不必等待立即返回就行,因为只是监测最小序号的信号对象之后,是否还有有信号的对象
          dwRet = WaitForMultipleObjects(nCount - nIndex,&pHandles[nIndex],false,0);   
          switch(dwRet)   
          {
            case WAIT_TIMEOUT:   
              nIndex = nCount; //退出检测,因为没有被触发的对象了.   
              break;
            case WAIT_FAILED:   
              return 1;   
            default:
            {
              nIndex = dwRet - WAIT_OBJECT_0;   
              ProcessHanlde(nIndex++); //自己编写函数,处理有信号的对象
            }
            break;
          }//switch(dwRet)结束
        }//while(nIndex < nCount) 结束
      }//default结束
      break;
    }//switch(dwRet)结束

  }//while(1)结束
  return 0;
}

 

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有