事件EVENT,WaitForSingleObject(),WaitForMultipleObjecct()和SignalObjectAndWait() 的使用(下)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/believe_s/article/details/81985122

注意:当WaitForMultipleObjects等待多个内核对象的时候,如果它的bWaitAll 参数设置为false。其返回值减去WAIT_OBJECT_0 就是参数lpHandles数组的序号。如果同时有多个内核对象被触发,这个函数返回的只是其中序号最小的那个。

如果bWaitAll 为TRUE 则等待所有信号量有效在往下执行。(FALSE 当有其中一个信号量有效时就向下执行)

问题就在这里,我们如何可以获取所有被同时触发的内核对象。

举个例子:我们需要在一个线程中处理从完成端口、数据库、和可等待定时器来的数据。一个典型的实现方法就是:用WaitForMultipleObjects等待所有的这些事件。如果完成端口,数据库发过来的数据量非常大,可等待定时器时间也只有几十毫秒。那么这些事件同时触发的几率可以说非常大,我们不希望丢弃任何一个被触发的事件。那么如何能高效地实现这一处理呢?   

MSDN中有一句非常重要的描述,它可以说是WaitForMultipleObjects用法的精髓:The function modifies the state of some types of synchronization objects. Modification occurs only for the object or objects whose signaled state caused the function to return. For example, the count of a semaphore object is decreased by one. When bWaitAll is FALSE, and multiple objects are in the signaled state, the function chooses one of the objects to satisfy the wait; the states of the objects not selected are unaffected.   

多个内核对象被触发时,WaitForMultipleObjects选择其中序号最小的返回。而WaitForMultipleObjects它只会改变使它返回的那个内核对象的状态。

这儿又会产生一个问题,如果序号最小的那个对象频繁被触发,那么序号比它大的内核对象将得不到被处理的机会。为了解决这一问题,可以采用双WaitForMultipleObjects检测机制来实现。见下面的例子:

DWORD WINAPI ThreadProc(LPVOID lpParameter)   
{   
    DWORD dwRet = 0;   
    int nIndex = 0;   
    while(1)   
    { 
        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事件对象总数   
                {   
                    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结束
                }//while结束
            }//default结束
                break;
        }//switch结束

    }//while结束
    return 0; 
}

SignalObjectAndWait()

你也可以同时通知一个内核对象,同时等待另一个内核对象,这两个操作以原子的方式进行。
语法

DWORD SignalObjectAndWait(
  HANDLE hObjectToSignal,//通知的内核对象
  HANDLE hObjectToWaitOn,//等待的内核对象
  DWORD  dwMilliseconds,//等待的时间
  BOOL   bAlertable//与IO完成端口有关的参数,暂不讨论
);

参数

  • hObjectToSignal
    要发信号的对象的句柄。此对象可以是信号量,互斥量或事件。
    如果句柄是信号量,则需要SEMAPHORE_MODIFY_STATE访问权限。如果句柄是事件,则需要EVENT_MODIFY_STATE访问权限。如果句柄是互斥锁且调用者不拥有互斥锁,则函数将失败并显示ERROR_NOT_OWNER。

  • hObjectToWaitOn
    要等待的对象的句柄。该SYNCHRONIZE访问权是必需的; 有关更多信息,请参阅同步对象安全性和访问权限 。有关可以指定其句柄的对象类型的列表,请参阅“备注”部分。

  • dwMilliseconds
    超时间隔,以毫秒为单位。即使对象的状态是非信号且没有完成或异步过程调用(APC)对象排队,该函数也会在间隔过去时返回。如果dwMilliseconds为零,则该函数测试对象的状态,检查排队的完成例程或APC,并立即返回。如果dwMilliseconds是INFINITE,则函数的超时间隔永远不会过去。

  • bAlertable
    如果此参数为TRUE,则该函数在系统对I / O完成例程或APC函数进行排队时返回,并且该线程调用该函数。如果为FALSE,则函数不返回,并且线程不调用完成例程或APC函数。
    当排队APC的函数调用完成时,完成例程排队。只有当bAlertable为TRUE且调用线程是排队APC的线程时,此函数才会返回并调用完成例程。

返回值

返回代码/值 描述
WAIT_ABANDONED/0x00000080L 指定的对象是在拥有线程终止之前由拥有互斥对象的线程未释放的互斥对象。互斥对象的所有权被授予调用线程,并且互斥锁被设置为无信号。如果互斥锁正在保护持久状态信息,则应检查它是否一致。
WAIT_IO_COMPLETION/0x000000C0L 等待由一个或多个排队到线程的用户模式 异步过程调用(APC)结束。
WAIT_OBJECT_0/0x00000000L 发出指定对象的状态信号。
WAIT_TIMEOUT/0x00000102L 超时间隔已过,对象的状态未发出信号。
WAIT_FAILED/(DWORD)0xFFFFFFFF 该功能失败了。要获取扩展错误信息,请调用 GetLastError。

备注
所述SignalObjectAndWait功能提供了一个更有效的方式来信号发送一个对象,然后在另一等待相比单独的函数调用诸如 SetEvent的随后WaitForSingleObject的。

该 SignalObjectAndWait函数可以等待以下对象:

  • 更改通知
  • 控制台输入
  • 事件
  • 内存资源通知
  • 互斥
  • 处理
  • 信号
  • 线

线程可以使用SignalObjectAndWait函数来确保工作线程在发信号通知对象之前处于等待状态。例如,线程和工作线程可以使用句柄来事件对象来同步它们的工作。该线程执行如下代码:

 dwRet = WaitForSingleObject(hEventWorkerDone, INFINITE);
  if( WAIT_OBJECT_0 == dwRet)
    SetEvent(hEventMoreWorkToDo);

工作线程执行如下代码:

dwRet = SignalObjectAndWait(hEventWorkerDone,
                              hEventMoreWorkToDo,
                              INFINITE, 
                              FALSE);

注意,“信号”和“等待”不能保证作为原子操作执行。在调用SignalObjectAndWait的线程开始等待第二个对象之前,在其他处理器上执行的线程可以观察第一个对象的信号状态。

在Windows 7中使用SignalObjectAndWait 和PulseEvent时要格外小心 ,因为在多个线程之间使用这些API会导致应用程序死锁。由信号发送线程SignalObjectAndWait 调用PulseEvent以发信号通知的等待对象SignalObjectAndWait呼叫。在某些情况下,SignalObjectAndWait的调用者无法及时接收等待对象的信号状态,从而导致死锁。

使用等待函数和直接或间接创建窗口的代码时要小心。如果一个线程创建了任何窗口,它必须处理消息。消息广播将发送到系统中的所有窗口。使用没有超时间隔的等待函数的线程可能会导致系统死锁。间接创建窗口的两个代码示例是DDE和COM CoInitialize。因此,如果您有一个创建窗口的线程,请务必从另一个线程调用SignalObjectAndWait。如果无法做到这一点,可以使用 MsgWaitForMultipleObjects或 MsgWaitForMultipleObjectsEx,但功能不相同。

要编译使用此函数的应用程序,请将 _WIN32_WINNT 定义为0x0400或更高版本。

猜你喜欢

转载自blog.csdn.net/believe_s/article/details/81985122