UI更改正在线程上更新,并且不确定哪个线程正在执行更新

时间:2011-12-01 15:59:35

标签: c# winforms

在给定的表单中,代码引用主窗体上的静态元素并进行更新。

SomeRandomForm.cs

MainForm.Instance.UpdateSomeLabel("hello world");

现在,更新主要表单的标签的静态实例(或者它可能是什么)的代码是否在UI线程上是不明显的。

我是否有可能以某种方式包装此方法调用(UpdateSomeLabel)以确保它仅在UI线程上更新?我想实际修改UpdateSomeLabel方法,以确保更新只发生在UI线程上。

只是尝试清理此应用以确保其安全。

3 个答案:

答案 0 :(得分:3)

您应该熟悉Invoke(...)BeginInvoke(...)方法,并确定此跨线程更新所需的语义。你想要发射并忘记,而不是等待消息泵回到你的文本更新?使用BeginInvoke(...)。在发生UI更新之前,您是否希望线程阻塞?使用Invoke(...)

另外,请记住,如果表单可能被处置或尚未显示,则在调用InvokeRequired之前,您需要检查表单上的Invoke(...)标志以外的内容。 BeginInvoke(...)。如果这种情况有可能发生,那么你的逻辑应该是这样的:

public void DoSomeUpdate(string text)
{
    if(this.IsDisposed || (!this.IsHandleCreated))
    {
        // error condition; run away!
        return;
    }

    if(this.InvokeRequired)
    {
        // BeginInvoke or Invoke here, based on the semantics you need
        return;
    }

    this.UpdateSomeLabel(text);
}

请注意,如果有任何不清楚的地方。

<强>更新

有关于检查控件的句柄是否已创建或是否已被处置的评论。

关于前一种情况(IsHandleCreated),在很多情况下,这绝对是你可以预先考虑的事情;然而,并非所有情况。如果您没有动态创建控件,并且您的后台线程内容正在通过UI线程触发,那么您可能很安全。然而...

如果有任何暗示后台线程任务可能在飞行中足够长的时间,你可以单击表单上的'X'并关闭它(从而处理它),那么你会得到一个很好的例外如果您在致电IsDisposedInvoke(...)之前未选中BeginInvoke(...)。它类似于(在C中)检查malloc(...)返回非零值的东西。它确实发生了。尽管单调乏味,但应用程序作者有义务关注这些潜在的错误案例。这确实增加了并发症;但是,这很重要,特别是如果你不希望你的应用程序崩溃。 在致电IsDisposedIsHandleCreated时未能正确说明Invoke(...)BeginInvoke(...)会让您面临崩溃应用的可能性。您可以避免只需几行代码即可崩溃。

答案 1 :(得分:1)

我见过的最优雅的方法是确保在UI线程上运行WinForms方法,这是本文中StyxRiver(接受的答案,向下滚动)的答案:

How to update the GUI from another thread in C#?

使用他提出的扩展方法,您可以紧凑而优雅地确保在UI线程上进行更新。

答案 2 :(得分:1)

FMM对他的回答发表了评论。我没有考虑到没有创建句柄或控件处理的可能性。被问到的问题(在另一篇文章中提到)是“最简单的方法是什么?”这个扩展非常简单,你不同意吗?

话虽这么说,我也只给了他一小段延期。也存在类似的“调用”。

using System;
using System.Windows.Forms;

public static class Extensions
{
    /// <summary>
    /// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
    /// </summary>
    /// <param name="this">The control responsible for performing the <param ref="code" /></param>
    /// <param name="code">The Action to be performed on the Control.</param>
    public static void UIThread(this Control @this, Action code)
    {
        // Check for error
        if (@this == null || !@this.IsHandleCreated || @this.IsDisposed)
        { return; }

        // Execute code
        if (@this.InvokeRequired)
        { @this.BeginInvoke(code); }
        else { code.Invoke(); }
    }

    /// <summary>
    /// Executes the Action on the UI thread, blocks execution on the calling thread until Action has been completed.
    /// </summary>
    /// <param name="this">The control responsible for performing the <param ref="code" /></param>
    /// <param name="code">The Action to be performed on the Control.</param>
    public static void UIThreadInvoke(this Control @this, Action code)
    {
        // Check for error
        if (@this == null || !@this.IsHandleCreated || @this.IsDisposed)
        { return; }

        // Execute code
        if (@this.InvokeRequired)
        { @this.Invoke(code); }
        else { code.Invoke(); }
    }
}

它甚至检查以确保首先创建了Control,而不是抛出可能的NullReferenceException,这可能是Extensions的可能性。