首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >事件中的调用和代理--为什么这些操作是以这种(意外的)顺序发生的?

事件中的调用和代理--为什么这些操作是以这种(意外的)顺序发生的?
EN

Stack Overflow用户
提问于 2015-02-19 18:59:07
回答 2查看 232关注 0票数 3

我有一个problem with component events,它可以用delegate来解决。

我以前没有和代表们合作过,所以我测试了一下,发现了这种奇怪的行为:

当使用委托时,操作发生在一个完全出乎意料的顺序中。

第一个示例:

代码语言:javascript
运行
复制
string Tracker = "";

private void button1_Click(object sender, EventArgs e)
{
    Tracker = "A";

    BeginInvoke((MethodInvoker)delegate
    {
        Tracker += "B";
    });

    Tracker += "C";
} 

在此之后,Tracker包含ACB。不知道它是如何工作的,但它似乎像描述的那样起作用。

第二个示例:

代码语言:javascript
运行
复制
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

原因是什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-02-19 19:05:19

样本1:

代码语言:javascript
运行
复制
private void button1_Click(object sender, EventArgs e)  
{
    Tracker = "A";

    BeginInvoke((MethodInvoker)delegate
    {
        Tracker += "B";
    });

   Tracker += "C";
} 

首先指定跟踪器,然后在GUI队列上异步推送委托,所以在有机会之前不会发生。所以下一个追踪器变成AC。然后GUI线程就自由了,因此您得到了ACB。样本2:

代码语言:javascript
运行
复制
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线程执行。

票数 4
EN

Stack Overflow用户

发布于 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类中的代码完成后运行。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28614664

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档