如何实现Win32 API GetMessage()的“阻塞”行为?

时间:2017-09-04 13:48:50

标签: winapi message-queue win32gui

根据hereGetMessage()是一个阻塞调用,直到可以从消息队列中检索到消息时才会返回。

那么, 阻止 行为是如何实现的呢?

GetMessage()是否使用某种自旋锁,以便UI线程忙等待显示在消息队列中的新消息?如果是这样,我想至少有一个CPU核心应该在UI应用程序运行时具有很高的使用率。但我没有看到这种情况发生。那它是如何运作的?

ADD 1

感谢评论中的提示。自旋锁意味着降低线程上下文切换的成本。它不应该在这里使用。我也想过这里可能会使用一些事件范例。但如果它是事件驱动的,那么这个事件模型是如何工作的呢?

我的猜测是这样的:

  • 定期引发输入检查事件。也许通过一些硬件定时器中断。然后定时器中断处理程序将检查输入事件的各种输入设备缓冲区。然后根据当前桌面上下文将其放入某个应用程序的消息队列中,例如哪个是活动窗口。

我想其他一些东西也可能基于定时器中断,例如线程上下文切换。

ADD 2

基于迄今为止的回复。 UI线程等待一些事件对象。但是由于UI线程正在等待某些东西,它不活跃并且它本身无法做任何事情。事件对象只是一些被动状态信息。所以 必须成为别人才能在事件状态发生变化时唤醒线程。我认为它应该是线程调度程序。并且线程调度程序可能通过定时器中断 脉冲

线程调度程序将定期检查事件状态并唤醒线程并根据需要将消息放入其队列中。

我对整个情况是否正确?

ADD 3

还有一个问题:谁修改了事件对象的状态?基于here,似乎事件只是一些可被任何修改的数据结构活跃 各方。我认为线程调度程序只是使用线程和事件之间的关系来决定运行哪个线程。

当一个线程被安排运行时,它的所有要求应该已经完成​​了。例如,一条消息应该被放入其队列 之前 它等待的事件被引发。这是合理的,否则可能为时已晚。 (感谢RbMm的评论。)

ADD 4

在JDK中,LinkedBlockingDeque类型也使用take()方法提供类似的阻止行为。

  

检索并删除此双端队列所代表的队列的头部   (换句话说,这个双端队列的第一个元素), 等待   在元素可用之前必须

.NET对应的是BlockingCollection< T >类型。一个thread来讨论它。

以下是关于how to implement a blocking queue in C#的帖子。

1 个答案:

答案 0 :(得分:5)

确切的实施是内部的,没有记录。

Windows中的大多数窗口管理器都是在内核模式下实现的。 GetMessage也不例外。它在内核模式下等​​待event object。当一个线程正在等待事件(或任何其他基于内核的synchronization object)时,它不会使用任何CPU时间,因为它不会被安排在等待满足之前运行。

当消息被发布到等待线程的消息队列,或者另一个线程向属于等待线程的窗口发送消息,或者等待线程中的计时器触发时,或者等待线程中的窗口准备好绘制,事件发出信号,线程唤醒,GetMessage()相应地动作,无论是将发布的消息返回给调用者,还是将发送的消息直接发送到目标窗口过程和然后回去等待活动。

如果您比较WaitForMultipleObjects和MsgWaitForMultipleObjects中可以等待的最大对象数,您可以看到泄露到公共API中的实现:

  

最大对象句柄数为MAXIMUM_WAIT_OBJECTS。

VS

  

最大对象句柄数为MAXIMUM_WAIT_OBJECTS减去   之一。