在给定的表单中,代码引用主窗体上的静态元素并进行更新。
SomeRandomForm.cs
MainForm.Instance.UpdateSomeLabel("hello world");
现在,更新主要表单的标签的静态实例(或者它可能是什么)的代码是否在UI线程上是不明显的。
我是否有可能以某种方式包装此方法调用(UpdateSomeLabel)以确保它仅在UI线程上更新?我想实际修改UpdateSomeLabel方法,以确保更新只发生在UI线程上。
只是尝试清理此应用以确保其安全。
答案 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'并关闭它(从而处理它),那么你会得到一个很好的例外如果您在致电IsDisposed
或Invoke(...)
之前未选中BeginInvoke(...)
。它类似于(在C中)检查malloc(...)
返回非零值的东西。它确实发生了。尽管单调乏味,但应用程序作者有义务关注这些潜在的错误案例。这确实增加了并发症;但是,这很重要,特别是如果你不希望你的应用程序崩溃。 在致电IsDisposed
或IsHandleCreated
时未能正确说明Invoke(...)
和BeginInvoke(...)
会让您面临崩溃应用的可能性。您可以避免只需几行代码即可崩溃。
答案 1 :(得分:1)
我见过的最优雅的方法是确保在UI线程上运行WinForms方法,这是本文中StyxRiver(不接受的答案,向下滚动)的答案:
How to update the GUI from another thread in C#?
使用他提出的扩展方法,您可以紧凑而优雅地确保在UI线程上进行更新。
答案 2 :(得分:1)
话虽这么说,我也只给了他一小段延期。也存在类似的“调用”。
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的可能性。