的WinForms没有响应消息循环 [英] WinForms message loop not responsive

查看:105
本文介绍了的WinForms没有响应消息循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我故意滥用在Windows消息循环窗体应用程序,但我的玩票工程进展迅速超出了我的理解程度。当任务运行时的形式没有响应。是的,有很多这样的其他问题,但对我来说,我刻意避开另一个线程的工作(打击自己赢得了赌注?)

I'm deliberately abusing the message loop in a Windows Forms application, but my "just for fun" project quickly progressed beyond my level of understanding. While the task is running the form is unresponsive. Yes, there are lots of other questions like this, but in my case I am deliberately avoiding work on another thread (to win a bet against myself?)

我有一个对于(许多)的简称UI线程的时间片运行的功能: get_IsComplete()检查任务已完成; 的DoWork()循环从0到1000(只是为了保持温暖CPU)。该任务通过调用启动 control.BeginInvoke(新动作(ContinueWith),控制); 于是它(尾递归)调用自身直到完成,始终运行的短片在UI线程上运行。

I have a function that runs for (many) short slices of time on the UI thread: get_IsComplete() checks if the task is complete; DoWork() loops from 0 to 1000 (just to keep the CPU warm). The task is started up by calling control.BeginInvoke(new Action(ContinueWith), control); whereupon it (tail recursively) calls itself until completion, always running a short slice of work on the UI thread.

public void ContinueWith(Control control)
{
    if (!IsComplete)
    {
        DoWork();
        OnNext(control);
        control.BeginInvoke(new Action(ContinueWith), control);
    }
    else
    {
        OnCompleted(control);
    }
}



我所期望的应用程序来处理其他事件(鼠标点击,控制重绘,形式举动等),但似乎我的电话越来越多,比我想的优先级。

I expected the application to process other events (mouse clicks, control repaints, form moves etc.) but it seems my calls are getting more priority than I'd like.

有什么建议?

推荐答案

该control.BeginInvoke()调用会将您通过委托内部队列,并调用PostMessage的()来唤醒消息循环,并留意。这就是获得第一的BeginInvoke去。任何输入事件(鼠标和键盘)也走在消息队列中,Windows把他们那里。

The control.BeginInvoke() call places the delegate you pass in an internal queue and calls PostMessage() to wake up the message loop and pay attention. That's what gets the first BeginInvoke going. Any input events (mouse and keyboard) also go on the message queue, Windows puts them there.

您没有指望的行为是在运行时代码张贴的消息被检索。它不只是离队的有一个的调用请求,并执行它,它循环直到整个调用队列被清空。你的代码的工作方式,该队列永远不会清空,因为调用ContinueWith()增加了一个调用请求。所以它只是不断循环和处理调用请求,从来没有得到周围从消息队列中检索更多的消息。或者换一种说法:这是抽的调用队列,而不是消息队列

The behavior you didn't count on is in the code that runs when the posted message is retrieved. It doesn't just dequeue one invoke request and executes it, it loops until the entire invoke queue is emptied. The way your code works, that queue is never emptied because invoking ContinueWith() adds another invoke request. So it just keeps looping and processing invoke requests and never gets around to retrieving more messages from the message queue. Or to put it another way: it is pumping the invoke queue, not the message queue.

的输入消息留在消息队列中,直到你的代码将停止增加更多的invoke请求和定期的消息循环抽恢复,你的代码将停止递归后。你的用户界面看起来冻结,而这需要的地方,因为油漆的事件也不会被交付。当消息队列为空,他们只得到生成的。

The input messages stay in the message queue until the your code stops adding more invoke requests and the regular message loop pumping resumes, after your code stops recursing. Your UI will look frozen while this takes place because Paint events won't be delivered either. They only get generated when the message queue is empty.

重要的是,它的工作方式是这样的PostMessage的()调用不能保证正常工作。 Windows不允许在消息队列超过10,000消息。但Control.BeginInvoke()没有这样的限制。通过完全排空调用队列,一个丢失PostMessage的消息不会引起任何问题。虽然此行为不会导致其他问题。一个经典的一个是调用BackgroundWorker.ReportProgress()过于频繁。同样的行为,UI线程只是充斥着调用请求,并围绕其正常职责没有得到了。不赞成任何人倒挂在运行到这个:我使用的BackgroundWorker,但我的UI的还是的冻结

It is important that it works the way it does, the PostMessage() call isn't guaranteed to work. Windows doesn't allow more than 10,000 message in the message queue. But Control.BeginInvoke() has no such limit. By emptying the invoke queue completely, a lost PostMessage message doesn't cause any problem. This behavior does cause other problems though. A classic one is calling BackgroundWorker.ReportProgress() too often. Same behavior, the UI thread is just flooded with invoke requests and doesn't get around its normal duties anymore. Frown upside down on anybody that runs into this: "I'm using BackgroundWorker but my UI still freezes".

安美居,您的实验九流失败。调用Application.DoEvents()将需要强制清空消息队列。与该警告的地段,检查详情这个答案。对于即将到来的支持的异步的关键字将提供另一种方式来做到这一点。事实并非如此肯定,如果它把消息优先级的不同。我相当怀疑,Control.BeginInvoke()是相当的核心。解决该问题的一个黑客是使用定时器用很短的时间间隔。定时器消息也走在消息队列(差不多),但他们有一个非常低优先级。输入事件得到优先处理。或低级别黑客:调用PostMessage的用自己的消息自己和压倒一切的WndProc检测到它。这是越来越有点过循规蹈矩。该Application.Idle事件是非常有用的检索任何输入事件之后做处理。

Anyhoo, your experiment is an abysmal failure. Calling Application.DoEvents() would be required to force the message queue to be emptied. Lots of caveats with that, check this answer for details. The upcoming support for the async keyword will provide another way to do this. Not so sure if it treats the message priority any differently. I rather doubt it, Control.BeginInvoke() is pretty core. One hack around the problem is by using a Timer with a very short Interval. Timer messages also go on the message queue (sort of) but they have a very low priority. Input events get processed first. Or a low level hack: calling PostMessage with your own message yourself and overriding WndProc to detect it. That's getting a bit off the straight and narrow. The Application.Idle event is useful to do processing after any input events are retrieved.

这篇关于的WinForms没有响应消息循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆