我有一个problem with component events,它可以用delegate
来解决。
我以前没有和代表们合作过,所以我测试了一下,发现了这种奇怪的行为:
当使用委托时,操作发生在一个完全出乎意料的顺序中。
第一个示例:
string Tracker = "";
private void button1_Click(object sender, EventArgs e)
{
Tracker = "A";
BeginInvoke((MethodInvoker)delegate
{
Tracker += "B";
});
Tracker += "C";
}
在此之后,Tracker
包含ACB
。不知道它是如何工作的,但它似乎像描述的那样起作用。
第二个示例:
string Tracker = "";
private void button2_Click(object sender, EventArgs e)
{
Tracker = "A";
MessageBox.Show(Tracker);
BeginInvoke((MethodInvoker)delegate
{
Tracker += "B";
MessageBox.Show(Tracker);
});
Tracker += "C";
MessageBox.Show(Tracker);
}
在此Tracker
之后还包含ACB
,但是消息框按以下顺序显示:
..。而不是我所期望的A
AC
ACB
。
原因是什么?
发布于 2015-02-19 19:05:19
样本1:
private void button1_Click(object sender, EventArgs e)
{
Tracker = "A";
BeginInvoke((MethodInvoker)delegate
{
Tracker += "B";
});
Tracker += "C";
}
首先指定跟踪器,然后在GUI队列上异步推送委托,所以在有机会之前不会发生。所以下一个追踪器变成AC。然后GUI线程就自由了,因此您得到了ACB。样本2:
string Tracker = "";
private void button2_Click(object sender, EventArgs e)
{
Tracker = "A";
MessageBox.Show(Tracker);
BeginInvoke((MethodInvoker)delegate
{
Tracker += "B";
MessageBox.Show(Tracker);
});
Tracker += "C";
MessageBox.Show(Tracker);
}
同样的事情也会发生:首先,消息框将出现在A中,我认为这是合乎逻辑的。接下来,委托被推送到GUI线程的队列上,然后Tracker变成AC,并且将messagebox调度为在GUI线程上运行,并调度以显示AC。但是,由于在GUI线程上调度了其他东西,所以另一件事将是第一件。因此,首先委托将执行,然后AC将显示。
如果我的解释在某个时候还不清楚,发表评论,并告诉我哪一部分需要更详细的阐述。但底线是将操作推送到GUI线程,并按GUI线程所显示的顺序由GUI线程执行。
发布于 2015-02-19 19:12:55
这需要了解UI线程的dispatcher循环是如何工作的。当您使用BeginInvoke()时,您会向该循环发送一条消息,该消息告诉它查看调用队列。只有在代码停止运行和Click事件处理程序返回之后才会处理该消息。因此,在Click事件处理程序停止运行之后,Tracker += "B";
语句将在稍后执行。
MessageBox类也使用dispatcher循环,但它是自己的,而不是与程序的dispatcher循环相关联的。让它成为情态。因此,Click方法在最后一个MessageBox.Show()调用中暂停,其dispatcher循环开始发送消息,这将触发被调用的方法。另一个MessageBox.Show()调用,您首先看到它。第二个呼叫的调度员被卡住,直到对话框关闭。
这是一种重入性,会导致很难诊断错误。这使得Application.DoEvents()方法非常危险。通过代码运行的顺序进行推理变得非常困难。MessageBox.Show()实际上相当于DoEvents()。但没有那么恶心,它的作用就像一个对话框,并禁用所有其他窗口,以尽量减少事故的次数。它消除了用户触发的事件以意外顺序运行的可能性。但并不能完全消除它们,它无法阻止代码。
FWIW,像这样使用BeginInvoke()非常有用。它可以解决由不稳定的类引起的许多问题,这些类会生成事件,但是当您在事件处理程序中“做太多”时就不会很好地响应。TreeView类是一个非常喜怒无常的类,非常引人注目。通过使用BeginInvoke(),您可以确保代码在balky类中的代码完成后运行。
https://stackoverflow.com/questions/28614664
复制相似问题