【问题标题】:Performance issues when updating UI without checking InvokeRequired first?在不先检查 InvokeRequired 的情况下更新 UI 时的性能问题?
【发布时间】:2010-10-12 22:27:11
【问题描述】:

我变得有点懒了(有时这很好)并开始通过调用回调而不先检查 InvokeRequired 来更新 WinForms UI。

是否有我应该注意的性能问题或注意事项?

    private delegate void SetStatusEventHandler(string statusMessage);
    private void SetStatus(string statusMessage)
    {
        Invoke((MethodInvoker) (() =>
        {
            resultLabel.Text = statusMessage;
        }));

        //  - vs -

        if (InvokeRequired)
        {
            SetStatusEventHandler cb = SetStatus;
            Invoke(cb, statusMessage);
        }
        else
        {
            resultLabel.Text = statusMessage;
        }
    }

[编辑]:大多数情况下,调用“invoke”的方法最多会被调用,例如每秒 10 到 20 次,中间间隔很宽.

[UPDATE]采用以下扩展方法解决

public static class SmartInvoker
{
    public static void InvokeHandler(this Control control, MethodInvoker del)
    {
        if (control.InvokeRequired)
        {
            control.Invoke(del);
            return;
        }
        del();
    }
}

...

    private void SetStatus(string statusMessage)
    {
        this.InvokeHandler(() => resultLabel.Text = statusMessage);
    }

我想找出如何管理扩展方法类是我需要深入研究的另一个主题。谢谢你的帮助

【问题讨论】:

    标签: c# winforms performance user-interface


    【解决方案1】:

    编辑:查看 cmets 就整个帖子与立即发送恶意软件的辩论。

    无论哪种方式,我的答案实际上是相同的:除非这种情况非常经常不必要地发生(即大部分时间你都在 UI 线程上开始,并且它附加到类似的东西鼠标移动处理程序)我不会担心。它确实使代码更简单。如果这个 会经常被调用,我会测量和测试更多:)

    Invoke 使用 EventHandlerMethodInvoker 委托比使用其他代理更快。我不知道这两者之间是否有任何区别 - 你可能想检查一下。

    您可以通过编写扩展方法来简化此操作,例如

    public static void InvokeHandler(this Control control, MethodInvoker handler)
    {
         control.Invoke(handler);
    }
    

    然后你就可以制作你的代码了:

    private void SetStatus(string statusMessage)
    {
        this.InvokeHandler(delegate
        {
            resultLabel.Text = statusMessage;
        });
    }
    

    private void SetStatus(string statusMessage)
    {
        InvokeHandler(() => resultLabel.Text = statusMessage);
    }
    

    这样你就不需要指定委托类型了。

    【讨论】:

    • 我不认为它会......我的印象是它只是调用它没有涉及事件队列
    • @Marc,我只是快速浏览了一下反射器代码,看起来调用将始终通过事件队列。不过没有做深入调查。
    • 不,它通过相同的 Control.MarshaledInvoke 代码路径。它将它放在队列中,但它会立即执行队列。
    • @Dustin,但不是马上。它会发布一条消息,然后等待。我的理解是这将导致泵发生,这意味着在调用 Inovke() 和执行实际委托之间可能会发生其他调用。
    • 谢谢 - 然后我撤回了我的帖子。但结果是一样的:它现在执行......
    【解决方案2】:

    为什么不直接添加一个扩展方法,这样您就不必再考虑它了?

    public static object SmartInvoke(this Control control, MethodInvoker del) {
      if ( control.InvokeRequired ) {
        control.Invoke(del);
        return;
      }
      del();
    }
    

    现在你的代码变成了

    private void SetStatus(string statusMessage) {
      this.SmartInvoke(() => resultLabel.Text = statusMessage);
    }
    

    【讨论】:

    • 如果您在控件本身内(我相信很可能),您将需要在 SmartInvoke 之前使用 this。 this.SmartInvoke(...);
    • @Samuel,在这种情况下“这个”是不必要的。
    • 我不确定是否应该考虑使用扩展方法。我想首先调查使用扩展方法是否是一个好主意。但这听起来像是一种非常懒惰的方式来处理我的问题。谢谢
    • @JaredPar,真的吗?没有这个,我似乎无法让它工作。并且返回类型应该是 void,不能将 void del() 隐式转换为 object。
    • @Sung,你为什么会认为这很懒?在应用程序的任何地方编写相同的代码是多余的,而且会浪费时间而不是节省时间。
    【解决方案3】:

    如果您已经在同一个线程上,我相信它只会防止不必要的帖子。因此,如果这将是最常见的情况(在正确的线程上),它可能会因不检查而导致性能下降,但我不认为它实际上是必需的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-24
      • 1970-01-01
      • 2012-12-16
      • 2012-10-06
      • 1970-01-01
      • 2016-03-27
      • 1970-01-01
      • 2011-12-27
      相关资源
      最近更新 更多