我的应用程序GUI有时会停止重绘。 有许多线程正在触发各种事件(如计时器或网络数据准备等)。还有很多控件正在订阅这些事件。因此,所有事件处理程序都会播放InvokeRequired / Invoke游戏。 现在我发现当GUI冻结时,很多线程都在等待Invoke()返回。看起来消息泵停止泵送。 处理程序看起来像这样:
private void MyEventHandler( object sender, EventArgs e ) {
if ( InvokeRequired ) {
Invoke( new EventHandler( MyEventHandler ), sender, e );
return;
}
SetSomeStateVariable();
Invalidate();
}
有什么想法吗?
解决方案:BeginInvoke()。看起来你应该总是使用BeginInvoke(),如果你有很多CrossThread-Events ......
感谢。
谢谢大家。
编辑:看起来BeginInvoke()
真的解决了它。直到现在都没有冻结。
答案 0 :(得分:27)
调用等待,直到在GUI线程中处理事件。如果您希望它是异步的,请使用BeginInvoke()
答案 1 :(得分:6)
也许死锁?您是否确保在持有锁时不会触发事件?
您是否可以在附加调试器的情况下看到这一点?如果是这样,请将其冻结,然后点击“暂停”按钮 - 并查看UI线程正在做什么。
请注意,如果您能够使用BeginInvoke而不是Invoke,生活会更容易,因为它不会阻止。
另请注意,您不需要“new EventHandler”位 - 只需
Invoke((EventHandler) MyEventHandler, sender, e);
应该没问题。
答案 2 :(得分:3)
通过观看这个问题,我可以看到你不会得到任何可以立即解决问题的答案,因为大多数答案都要求你调试事件,并且它很少发生,这几乎是不可能的。因此,我建议您进行一些代码更改,以帮助您确定该领域的罪魁祸首。
我建议您创建一个静态类,其唯一目的是处理所有的Invoke调用。我建议这个类有一个方法,它接受一个Control(调用Invoke)一个Action(要调用的方法)和一个描述(包含你需要知道的信息来识别方法及其含义要去做。)
在此方法的正文中,我建议您将此信息(方法,说明)排入队列并立即返回。
队列应由单个线程提供服务,该线程将动作/消息对从队列中弹出,在一对属性中记录当前时间和Action的描述,然后调用Action()。当Action返回时,将清除描述和时间(您的DateTime可以为空,或将其设置为DateTime.Max)。请注意,由于所有Invokes都会一次一个地编组到UI线程上,因此您不会通过单个线程在此处为队列服务而丢失任何内容。
现在,我们已经到了这一点。我们的Invoking类应该有一个心跳System.Threading.Timer线程。这不应该是一个windows.forms.timer对象,因为它在UI线程上运行(当ui被阻止时会被阻止!!!)。
此计时器的工作是定期查看当前Action被调用的时间。如果DateTime.Now - BeginTime> X,心跳计时器将决定此操作已被阻止。心跳计时器将记录(但是您记录)为该操作记录的描述。现在,您可以记录UI锁定时发生的情况,并可以更好地进行调试。
我知道这不是你问题的答案,但至少通过这样做,你可以很好地了解被阻止时发生的事情。
答案 3 :(得分:0)
已经提出了最可能的答案(死锁)。
模拟此行为的另一种方法是减少池线程和IO完成端口的数量;你有没有机会打电话给ThreadPool.SetMaxThreads(...)
?