【问题标题】:Strategy for waiting for two near-synchronous events等待两个近同步事件的策略
【发布时间】:2011-07-20 19:43:26
【问题描述】:

我正在编写一个使用 Win32 API 处理击键事件的库。每次击键发生时,两个独立的事件几乎同时发生(thisthis)。在我的库中,我有两个单独的线程,一个用于每种事件(hook 和 raw)。

线程 1(钩子)等待线程 2(原始)在继续之前获取其数据。它目前使用类似屏障的机制来实现这一点。因此,这两个事件都必须在其中任何一个事件被实际处理之前进入。到目前为止一切顺利。

但是有一个问题(当然)。在一个理想的世界里,我可以保证一直得到这两个事件。不幸的是,由于我不明白的原因,有时 Windows 决定不向我提供这两个事件中的一个(通常另一个应用程序暂时与输入有关)。因此,如果我输入“Hello world”,线程 1 可能正在处理“H”,而线程 2 可能从未收到“H”事件,并跳到“e”。所以事件失去了同步,一切都乱了套。

基本上我想要的是:我想以一种有意义的方式配对事件。如果线程 1 得到一个“H”事件,而线程 2 得到一个“e”,它应该 (1) 尝试等待正确的“H”事件,或者 (2) 超时(是的,线程 1 可以正常失败如果必须)。由于我知道事件都应该在某个时间窗口内出现,我想这使它成为一项实时编程任务。

我对实时编程一无所知。是否已经有解决方案/数据结构?如果是这样,它们是什么?如果不是,那么解决此类问题的一般方法是什么(保持两个暂时关闭的事件同步)?

谢谢。

【问题讨论】:

  • 你为什么要等两个?
  • 一个 API 让我可以控制击键的位置,另一个让我确定它来自哪个键盘。我希望能够同时做这两件事(根据哪个键盘发送输入来控制输入的位置)。正是这种匹配事件现在令人头疼。
  • 在任何情况下,WM_KEYUP/WM_KEYDOWN 消息肯定只会进入具有输入焦点的窗口...?
  • 不,我做的事情比这低得多。我正在使用原始输入 API 和全局钩子——它与焦点窗口毫无关系。顺便我得到的事件是WM_INPUT
  • 对于那些病态好奇的人,you can check out the project

标签: windows multithreading winapi real-time


【解决方案1】:

我不是 win32 方面的专家,但我不禁觉得,如果你输掉比赛,无论如何都不会完全可靠。据我了解,您正在尝试根据原始物理键盘控制键盘事件的去向(即它们去往哪个窗口) - 也许最好不要过滤事件,而是简单地将它们注入第一个地点。

也就是说,注册一个low-level keyboard hook,它会简单地删除所有注入的事件。现在使用您的原始键盘事件处理程序将模拟的 WM_KEYUP/WM_KEYDOWN 消息注入到最终应该接收它们的任何窗口。

但是,如果做不到这一点,我们将需要(以某种方式)同步 WM_INPUT 和 WM_KEY* 事件流。我假设您已经知道如何在注入的钩子 DLL 和“主”进程中的 WM_INPUT 侦听器之间共享数据。我们将从定义熟密钥流互斥体开始。处理 WM_KEY* 钩子的应用程序将在进行进一步处理之前立即使用此互斥体,以避免与可能也在处理密钥的其他进程竞争。

您的 WM_INPUT 处理进程可以开始将通过 WM_INPUT 接收的键码按顺序写入共享内存环形缓冲区(由命名互斥锁同步)。每个键码都应该有一个时间戳; WM_KEY* 挂钩将忽略旧的键码。 WM_KEY* 处理程序将首先检查他们的事件是否与缓冲区中最旧的未过期事件匹配;如果是,则应向主进程发送通知,并且挂钩将执行所需的任何处理。没问题。

如果环形缓冲区中没有数据,可以使用命名信号量来休眠。信号量的计数应该是环形缓冲区中剩余条目数的乐观计数;获取计数使进程有权和有义务从缓冲区中取出一项(如果存在),但如果缓冲区为空,则该进程必须丢弃环互斥体并返回等待信号量。然后钩子程序可以在互斥体上等待(短!)超时;如果超时到期,则钩子应认为存在不同步,并继续进行正常处理。然后让钩子在短时间内禁用自身可能是个好主意,以避免减慢快速键入的速度。

如果挂钩从环形缓冲区中读取的键码与发送的键击不匹配,则说明某处存在不同步。对此的处理可能取决于您在做什么;例如,如果您正在实现一个热键系统,一种方法是简单地删除所有原始事件,然后将挂钩变成纯粹的传递,直到所有键盘在短时间内静音。

请记住,由于您正在处理不同步的线程,每个线程都看到多个不同步的事件流的不同部分,因此不同步将始终是一个问题。事件注入可能仍然是避免这种情况的好方法,因为它为您提供了一个可以提供事件排序的同步点。

【讨论】:

  • 在这一点上,我不一定追求“完全可靠”,而是追求“最终一致”。
  • 考虑一下:用户键入“aaba”。中间的“ab”在原始输入处理程序中丢失了。因此,您看到“aaba”的 WM_KEY* 事件,但“aa” 的 WM_INPUT 事件。您将这些与单词的开头相匹配。现在用户输入“香蕉”,事情变得更加混乱。由于消息没有序列号,如果您丢失事件,您感到困惑。我想问题是这种混乱的危害有多大。
  • 哦,相信我,我已经很困惑了 :) 不是问题的症结在于:我知道这两个事件都应该在一定时间内进入系统.我能否以某种方式利用这一点并将事件过滤到暂时最接近的事件?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多