【发布时间】:2017-05-29 23:02:42
【问题描述】:
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/sec\n", nIterations / nSec);
return 0;
}
在 3.85GHz Ryzen 1800X 上,我每秒执行 7209623.405 次操作,这意味着平均花费 534 个 CPU 时钟(或 138.7 纳秒)来检查事件是否已设置。
但是,我想在对性能至关重要的代码中使用该事件,在该代码中,大多数时间实际上设置了事件,所以它只是对特殊情况的检查,在这种情况下,控制流会转到不是性能的代码-critical(因为这种情况很少见)。
由于安全属性和名称,我知道(使用CreateEvent 创建)的WinAPI 事件是重量级的。它们用于进程间通信。也许WaitForSingleObject() 太慢了,因为它从用户模式切换到内核模式并返回,即使设置了事件也是如此。此外,对于手动和自动重置事件,此函数的行为必须不同,并且检查事件类型也需要时间。
我知道可以使用 atomic_flag 实现快速用户模式互斥锁(自旋锁)。它的旋转循环可以用std::this_thread::yield() 扩展,以便让其他线程在旋转时运行。
对于事件,我不希望完全等效于自旋锁,因为当事件未设置时,可能需要大量时间才能再次设置。如果需要事件集的每个线程都开始旋转直到它再次被设置,那将是巨大的 CPU 电力浪费(尽管如果他们调用 std::this_thread::yield 不会影响系统性能)
所以我更喜欢一个临界区的类比,它通常只在用户模式下工作,当它意识到它需要等待(在自旋之外)时,它会切换到内核模式并等待一个繁重的同步对象像互斥锁一样。
UPDATE1:我发现 .NET 有 ManualResetEventSlim ,但在 WinAPI / C++ 中找不到等价物。
UPDATE2:因为请求了事件使用的详细信息,所以在这里。我正在实现一个可以在常规模式和维护模式之间切换的知识库。一些操作是仅维护的,一些操作是常规的,一些可以在两种模式下工作,但其中一些在维护中更快,一些在常规模式下更快。在开始时,每个操作都需要知道它是处于维护模式还是常规模式,因为逻辑发生了变化(或者操作根本拒绝执行)。用户可以不时请求在维护模式和常规模式之间切换。这是罕见的。当此请求到达时,旧模式下的新操作无法启动(请求失败),应用程序等待旧模式下的当前操作完成,然后切换模式。所以轻事件是这个数据结构的一部分:除了模式切换之外的操作要快速,所以需要快速设置/重置/等待事件。
【问题讨论】:
-
So I would rather like an analogy of a critical section- 为什么不使用它或者说新的 Slim Reader/Writer Locks 呢?或者你需要进程间同步? -
这是自动重置还是手动重置事件?是否有多个线程在等待事件?
-
windows中存在不同的同步功能,它们尽量在用户模式下完成大部分工作,只有在需要等待时才进入内核——临界区、slim rw锁、等待地址更改等。用于进程同步。当然可以实现基于联锁操作的自定义同步解决方案,并在需要等待时等待事件。但这只有在没有与您的任务匹配的情况下才有意义。因为不清楚您的真正任务以及需要同步的内容,所以现在给出更具体的建议。内核模式对象需要进入内核,这是一个繁重的操作
-
IOCP(输入/输出完成端口)?
-
@RbMm ,我不需要进程间同步。您能否详细说明(可能在代码示例的答案中)Slim Reader/Writer Lock 如何替换事件?
标签: c++ performance winapi events synchronization