1

我一直在尝试理解事件循环(不太顺利),并且我读过 Windows 消息传递循环是单线程的。如果是,Application.DoEvents 如何工作?事件循环不是一次处理一条消息并在处理每条消息/事件时阻塞吗?消息事件循环是否需要存在于与处理 Application.DoEvents 消息的线程不同的线程上?如果有单独的线程,我们将哪个线程称为“主”线程?我确定我错过了一些非常简单的东西,我只是不知道它是什么。

4

1 回答 1

1

我花了一天的大部分时间来解决这个问题(如果我说的有什么错误,请发表评论并让我知道,以便我纠正它)。实际上,我必须自己构建一个旧的 Win32 应用程序并创建消息循环(我是一个非常持久的 SOB)。所以有一个名为 WinMain 的函数会启动消息循环,如下所示:

while (GetMessage(&msg, NULL, 0, 0))
{
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

关于 GetMessage() 的事情是它会阻塞,直到消息队列中有可用的消息。如果您运行一个窗口应用程序并只是坐在那里查看窗口(不要导致任何将消息发布到队列的操作),主线程(创建窗口的线程)将在 GetMessage() 上暂停. 现在,当确实发布了一条消息时,我们进入了 while 循环(即如果消息没有退出,则为 0)。DispatchMessage() 是这里有趣的函数。此函数最终将导致(在 .NET 中)由 EventHandler 的控件和执行引发的事件。令我疑惑的是,如果调用栈是GetMessage()/DispatchMessage()/.../EventHandler,Application.DoEvents()怎么可能处理消息呢?嗯,这很简单。Win32 中的 DoEvents 如下所示:

void DoEvents()
{
   MSG msg;
   HACCEL hAccelTable;

   hAccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDC_TESTWIN32));

   // Main message loop:
   while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0)
   {
       if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
       {
           TranslateMessage(&msg);
           DispatchMessage(&msg);
       }
   }
}

所以 DoEvents() 实际上在初始消息循环的 DispatchMessage() 中启动了另一个循环来处理事件!关键区别在于,我们不是使用 GetMessage() 阻塞直到队列中有消息,而是使用 PeekMessage() 返回 0 并在队列中不再有任何消息时存在循环。

那么如果我们点击一​​个按钮两次并且在那个按钮的 EventHandler 中我们有一个 DoEvents() 调用呢?初始事件循环将处理第一次单击并触发事件。当 EventHandler 正在执行时,在 DoEvents() 调用中,事件将被第二次触发,并且 EventHandler 将再次进入(有点像递归调用)。这太可怕了!

所以最后,一切都发生在一个线程中,并且 DoEvents() 实际上阻塞,直到所有消息都被处理然后返回。现在我要睡几天。

于 2011-11-20T04:59:28.163 回答