我一直在尝试理解事件循环(不太顺利),我已经读过Windows消息循环是单线程的。如果是,Application.DoEvents如何工作?事件循环不会一次处理一条消息并在处理每条消息/事件时阻塞吗?消息事件循环是否需要存在于与处理Application.DoEvents的消息的线程不同的线程上?如果有单独的线程,那么我们称之为“主”线程?我确定我错过了很简单的东西,我只是不知道它是什么。
答案 0 :(得分:0)
我花了一天的时间来解决这个问题(如果我说错了,请评论并告诉我,以便我能纠正它)。我实际上必须构建一个旧的Win32应用程序并自己创建消息循环(我是一个非常持久的SOB)。所以有一个名为WinMain的函数启动消息循环,如下所示:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
关于GetMessage()的事情是它会阻塞,直到消息队列中有消息可用。如果您运行Windowed应用程序并且只是坐在那里看窗口(不要导致任何将消息发送到队列的操作),则主要线程(创建窗口的线程)在GetMessage()上暂停。现在当消息发布时,我们进入while循环(即如果消息不是退出,则为0)。 DispatchMessage()是这里有趣的功能。此函数最终将导致(在.NET中)事件由控件和EventHandlers的执行引发。令我困惑的是,如果调用堆栈是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);
}
}
}
因此,在初始消息循环的DispatchMessage()中,DoEvents()实际上启动了另一个循环来处理事件! 关键的区别在于,在队列中有消息之前,不使用阻塞的GetMessage(),而是使用PeekMessage()返回0并在队列中不再有任何消息时存在循环。强>
那么如果我们点击一个按钮两次并在该按钮的EventHandler中我们有一个DoEvents()调用呢?初始事件循环将处理第一次单击并触发事件。当EventHandler正在执行时,在DoEvents()调用时,事件将被第二次触发,并且将再次输入EventHandler(类似于递归调用)。那太吓人了!
所以最后,一切都发生在一个线程中,DoEvents()实际上会阻塞,直到所有消息都被处理然后返回。现在我要睡几天了。