Winapi / C 中的轻型事件
Light event in WinAPI / C++
Winapi/c 中是否有一些照明事件?特别是,我有兴趣最大程度地减少设置事件时所花费的时间(例如WaitForSingleObject()
(。这是一个代码示例,可以进一步澄清我的意思:
#include <Windows.h>
#include <chrono>
#include <stdio.h>
int main()
{
const int64_t nIterations = 10 * 1000 * 1000;
HANDLE hEvent = CreateEvent(nullptr, true, true, nullptr);
auto start = std::chrono::high_resolution_clock::now();
for (int64_t i = 0; i < nIterations; i++) {
WaitForSingleObject(hEvent, INFINITE);
}
auto elapsed = std::chrono::high_resolution_clock::now() - start;
double nSec = 1e-6 * std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
printf("%.3lf Ops/secn", nIterations / nSec);
return 0;
}
在3.85GHz ryzen 1800x上,我每秒获得7209623.405操作,这意味着534 CPU时钟(或138.7纳秒(平均用于检查是否设置了该事件。
但是,我想在大多数情况下实际设置该事件中的事件,因此实际上是对特殊情况的检查 - 临界(因为这种情况很少(。
Winapi事件(使用CreateEvent
创建(是重量重量,因为安全属性和名称。它们旨在用于过程间沟通。也许WaitForSingleObject()
太慢了,因为即使设置了事件,它也会从用户转换为内核模式并返回。此外,此功能对于手动和自动重置事件的行为必须有所不同,并且该事件类型的检查也需要时间。
我知道可以使用atomic_flag
实现快速的用户模式Mutex(旋转锁(。它的旋转环可以用std::this_thread::yield()
扩展,以便在旋转时运行其他线程。
在事件中,我不希望完全等同于旋转锁,因为当未设置事件时,可能需要大量时间才能再次设置。如果每个需要事件集的线程开始旋转直到再次设置,那将是CPU电力的史诗般的浪费(尽管如果他们调用std::this_thread::yield
,则不会影响系统性能(
因此,我宁愿一个关键部分的类比,通常只能在用户模式下进行工作,并且当意识到它需要等待(旋转(时,它会切换到内核模式并在重型同步对象上等待像一个互惠。
Update1:我发现.NET具有ManualResetEventSlim
,但在Winapi/C 中找不到同等的。
update2:由于请求事件用法的详细信息,因此它们是。我正在实施一个可以在常规和维护模式之间切换的知识库。有些操作是仅维护的,有些操作是常规的,有些操作可以在两种模式下工作,但是其中一些操作的维护速度更快,有些则在常规模式下更快。开始后,每个操作都需要知道它是在维护还是常规模式下,随着逻辑的变化(或操作完全拒绝执行(。用户可以不时地在维护和常规模式之间请求切换。这很少见。当此请求到达时,在旧模式下没有新的操作可以启动(这样做的请求会失败(,并且该应用程序在旧模式下等待当前操作以完成,然后它切换模式。因此,轻事件是此数据结构的一部分:除模式切换以外的操作必须快速,因此他们需要快速设置/重置/等待事件。
从Win8开始使用WaitOnAddress
的最佳解决方案(WaitForSingleObject
,WakeByAddressAll
(例如SetEvent
(( notificationEventEvent (和WakeByAddressSingle
(工作如 synchronizationevent (。更多读取 - 读取 - WaitonAddress允许您创建一个同步对象
实施可以是下一个:
class LightEvent
{
BOOLEAN _Signaled;
public:
LightEvent(BOOLEAN Signaled)
{
_Signaled = Signaled;
}
void Reset()
{
_Signaled = FALSE;
}
void Set(BOOLEAN bWakeAll)
{
_Signaled = TRUE;
(bWakeAll ? WakeByAddressAll : WakeByAddressSingle)(&_Signaled);
}
BOOL Wait(DWORD dwMilliseconds = INFINITE)
{
BOOLEAN Signaled = FALSE;
while (!_Signaled)
{
if (!WaitOnAddress(&_Signaled, &Signaled, sizeof(BOOLEAN), dwMilliseconds))
{
return FALSE;
}
}
return TRUE;
}
};
不要忘记为链接器输入添加Synchronization.lib
。
该新API的代码非常有效,它们不会为等待(如事件(创建内部内核对象,而是使用新的API ZwAlertThreadByThreadId
ZwWaitForAlertByThreadId
特殊设计。
在Win8之前,如何实施它?首先看琐碎的 - 布伦·可罗运 事件句柄。并且必须看起来像:
void Set()
{
SetEvent(_hEvent);
// Sleep(1000); // simulate thread innterupted here
_Signaled = true;
}
void Reset()
{
_Signaled = false;
// Sleep(1000); // simulate thread innterupted here
ResetEvent(_hEvent);
}
void Wait(DWORD dwMilliseconds = INFINITE)
{
if(!_Signaled) WaitForSingleObject(_hEvent);
}
但是此代码确实不正确。我们在Set
(Reset
(中进行2操作的问题 - 更改_Signaled
和_hEvent
的状态。从用户模式进行原子/互锁操作,这绝不是这样做的。这意味着可以在这两个操作之间中断线程。假设并发调用Set
和Reset
中的2个不同的线程。在大多数情况下,操作将以下一个顺序执行:
SetEvent(_hEvent);
_Signaled = true;
_Signaled = false;
ResetEvent(_hEvent);
这里都可以。但可能会和下一个订单(未点击一个Sleep
进行测试(
SetEvent(_hEvent);
_Signaled = false;
ResetEvent(_hEvent);
_Signaled = true;
结果_hEvent
将处于重置状态,当_Signaled
为true
。
将其作为原子本身实现,而无需执行操作系统支持,这将不可能是多么可能。但是我首先要寻找这一点 - 为什么?像行为这样的事件是否正是您需要进行任务?
如果您可以放弃Windows 7的支持。
,另一个答案非常好。在Win7上,如果您从多个线程设置/重置事件,但是只需要很少睡觉,建议的方法很慢。
相反,我使用的是由关键部分保护的布尔值,条件变量可唤醒/睡眠。
等待方法将在SleepConditionVariablecs API上进入内核,这是预期的和您想要的。
但是设置&amp;重置方法将完全在用户模式下工作:设置单个布尔变量非常快,即在99%的情况下,关键部分将执行其用户模式锁定免费魔法。
- Android NDK传感器向事件队列报告奇怪的间隔
- 从文本文件中读取时钟时间和事件时间并进行处理
- WMI检测进程创建事件-c++
- EvtExportLogneneneba API正在将远程计算机的事件日志保存到远程PC本身.如何将其保存到主机
- 处理闪烁窗口事件
- C++Builder中的OnClick事件签名存在问题
- 跟踪滚动条上的鼠标事件
- 什么是事件表 (wxWidgets)?
- 如何在 MFCaptureEngine 中获取"Camera removed"事件
- 给定顺序中的事件处理
- 当服务中的事件被触发时,如何将响应从服务发送回客户端?
- 在 C++/CLI 中将 .NET 事件从一个 DLL 引发到另一个 DLL
- 如何创建事件驱动的 SDL2 应用程序
- Windows 进程间同步类似事件?
- 如何从C++端挂接到 QML 项的 onClick 事件
- 如何通过多类"Union variable" (sfml) 使用轮询事件
- 如何在Qt 4.8中阻止/忽略/丢弃早于特定超时的用户输入事件
- C++ 信号和插槽不工作:插槽不响应事件
- 在虚幻引擎中触发C++ dll的事件
- Winapi / C 中的轻型事件