Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/164.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 等待事件的线程并不总是捕获事件信号_C++_Windows_Multithreading_Event Handling - Fatal编程技术网

C++ 等待事件的线程并不总是捕获事件信号

C++ 等待事件的线程并不总是捕获事件信号,c++,windows,multithreading,event-handling,C++,Windows,Multithreading,Event Handling,我有一个应用程序,其中多个线程等待同一事件对象发出信号。我看到的问题似乎是一种争用条件,因为有时一些线程的等待状态(WaitForMultipleObjects)由于事件信号而返回,而其他线程的等待状态显然看不到事件信号,因为它们不返回。这些事件是使用CreateEvent作为手动重置事件对象创建的 我的应用程序处理这些事件,以便在向事件对象发送信号时,其“所有者”线程负责重置事件对象的信号状态,如下面的代码段所示。等待同一事件的其他线程不会尝试重置其信号状态 switch ( dwObject

我有一个应用程序,其中多个线程等待同一事件对象发出信号。我看到的问题似乎是一种争用条件,因为有时一些线程的等待状态(
WaitForMultipleObjects
)由于事件信号而返回,而其他线程的等待状态显然看不到事件信号,因为它们不返回。这些事件是使用
CreateEvent
作为手动重置事件对象创建的

我的应用程序处理这些事件,以便在向事件对象发送信号时,其“所有者”线程负责重置事件对象的信号状态,如下面的代码段所示。等待同一事件的其他线程不会尝试重置其信号状态

switch ( dwObjectWaitState = ::WaitForMultipleObjects( i, pHandles, FALSE, INFINITE ) )
{
case WAIT_OBJECT_0 + BAS_MESSAGE_READY_EVT_ID:
    ::ResetEvent( pHandles[BAS_MESSAGE_READY_EVT_ID] );
    /* handles the event */
    break;
}
换句话说,我所看到的问题似乎与以下描述有关:

如果调用PulseEvent发生 在线程已运行的时间内 已从等待状态中删除,则 线程将不会被释放,因为 PulseEvent仅释放这些线程 那是在等待它的时刻 打电话。因此,PulseEvent是 不可靠,不应由用户使用 新的应用程序。相反,使用 条件变量

如果是这样的话,我能看到的唯一解决方案是每个线程向对象的所有者线程注册其对给定事件对象的使用,以便所有者线程可以确定何时可以安全地重置事件对象的信号状态

switch ( dwObjectWaitState = ::WaitForMultipleObjects( i, pHandles, FALSE, INFINITE ) )
{
case WAIT_OBJECT_0 + BAS_MESSAGE_READY_EVT_ID:
    ::ResetEvent( pHandles[BAS_MESSAGE_READY_EVT_ID] );
    /* handles the event */
    break;
}

有更好的方法吗?谢谢。

是的,有更好的方法:

[…]相反,使用条件变量


具体查找

使用PulseEvent描述中的条件变量。唯一的问题是windows上的本机条件变量是从Vista开始实现的,所以像XP这样的旧系统没有它。但是您可以使用其他一些同步对象()来模拟条件变量,但我认为最简单的方法是使用boost库中的条件变量及其notify_all方法来唤醒所有线程()

另一种可能性(但不是很好)是为每个线程创建一个事件,当您现在有PulseEvent时,您可以为所有线程调用SetEvent。对于此解决方案,自动重置事件可能会更好。

为什么PulseEvent()不可靠以及没有它该怎么办 自动复位事件是国王

PulseEvent仅出现在Windows NT 4.0中。它在最初的WindowsNT3.1中不存在。相反,从Windows NT开始,确实存在CreateEvent、SETVEATE和WAITFrimeType对象等可靠的功能,所以请考虑使用它们。p> CreateEvent函数具有bManualReset参数。如果此参数为TRUE,该函数将创建一个手动重置事件对象,这需要使用ResetEvent函数将事件状态设置为无信号。这不是你需要的。如果此参数为FALSE,则函数将创建一个自动重置事件对象,并且在释放单个等待线程后,系统会自动将事件状态重置为无信号状态

这些自动重置事件非常可靠且易于使用

如果使用WaitForMultipleObjects或WaitForSingleObject等待自动重置事件对象,则它会在退出这些等待函数时可靠地重置事件

因此,请按以下方式创建事件:

EventHandle := CreateEvent(nil, FALSE, FALSE, nil);
等待来自一个线程的事件,并从另一个线程执行SetEvent。这是非常简单和非常可靠的

永远不要调用ResetEvent(因为它会自动重置)或PulseEvent(因为它不可靠且已弃用)。甚至微软也承认不应该使用PulseEvent。看

此函数不可靠,不应使用,因为只有在调用PulseEvent时处于“等待”状态的线程才会收到通知。如果它们处于任何其他状态,则不会通知它们,并且您可能永远无法确定线程状态是什么。在同步对象上等待的线程可以通过内核模式异步过程调用暂时从等待状态中移除,然后在APC完成后返回到等待状态。如果对PulseEvent的调用发生在线程已从等待状态移除的时间内,则不会释放该线程,因为PulseEvent仅释放在调用时正在等待的线程

您可以通过以下链接了解有关内核模式异步过程调用的更多信息:

我们从未在应用程序中使用过PulseEvent。关于自动重置事件,我们从WindowsNT3.51开始使用它们,它们工作得非常好

当多个线程等待单个对象时该怎么办 不幸的是,你的案子有点复杂。您有多个线程在等待一个事件,您必须确保所有线程都确实收到了通知。除了为每个线程创建自己的事件之外,没有其他可靠的方法

您写道“我能看到的唯一解决方案是每个线程向对象的所有者线程注册其对给定事件对象的使用”。这是正确的

您还写道,“所有者线程可以确定何时重置事件对象的信号状态是安全的”——这是不切实际和不安全的。最好的方法是使用自动重置事件,这样它们会自动重置自己

因此,您将需要与线程一样多的事件。除此之外,您还需要保留已注册线程的列表。因此,要通知所有线程,必须在循环中对所有事件句柄执行SetEvent。这是一种非常快速、可靠和廉价的方法。事件比线程便宜得多。因此,线程的数量是一个问题,而不是事件的数量。内核对象实际上没有限制-内核句柄的每个进程限制为2^24

是吗