C# 为什么我需要UI线程检查
要从其他线程更新UI,需要调用dispatcher的BeginInvoke方法。在调用方法之前,可以检查调用线程是否与调度程序关联 对于我的例子,我有两种方法来更新文本框;通过单击按钮和超时。守则:C# 为什么我需要UI线程检查,c#,wpf,multithreading,user-interface,C#,Wpf,Multithreading,User Interface,要从其他线程更新UI,需要调用dispatcher的BeginInvoke方法。在调用方法之前,可以检查调用线程是否与调度程序关联 对于我的例子,我有两种方法来更新文本框;通过单击按钮和超时。守则: using System; using System.Timers; using System.Windows; using System.Windows.Controls; namespace WpfApplication1 { public partial class MainWind
using System;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
private int i = 0;
private TextBlock myText = new TextBlock();
private Button myButton = new Button();
private Timer timer = new Timer(2 * 1000);
private StackPanel panel = new StackPanel();
public MainWindow()
{
InitializeComponent();
myButton.Content = "Click";
panel.Children.Add(myText);
panel.Children.Add(myButton);
this.AddChild(panel);
myButton.Click += (_, __) => IncrementAndShowCounter();
timer.Elapsed += (_, __) => IncrementAndShowCounter();
timer.Start();
}
private void IncrementAndShowCounter()
{
i++;
if (this.Dispatcher.CheckAccess())
{
myText.Text = i.ToString();
}
else
{
this.Dispatcher.BeginInvoke((Action)(() =>
{
myText.Text = i.ToString();
}));
}
}
}
}
当我不选中Access()并且总是执行BeginInvoke时,一切都正常
所以我的问题是为什么不总是使用BeginInvoke并跳过CheckAccess
所以我的问题是为什么不总是使用BeginInvoke
并跳过
检查访问权限
如果需要调用(即,您正在接触另一个线程拥有的控件),那么在大多数情况下您都应该这样做。如果不需要调用,则应跳过这两个步骤
使用CheckAccess
意味着您的代码不知道或不想假设它将在“正确”线程上运行。这有两个主要原因:通用性(您的代码在库中,您无法预测它将如何使用)和方便性(您只需要一种方法来处理这两种情况,或者您希望在不中断程序的情况下自由更改操作模式)
您的示例属于第二类:相同的方法为两种操作模式提供服务。在这种情况下,您有三种可能的选择:
检查访问
这将给您带来性能上的影响(这里的影响可以忽略不计),并且它还将使代码的读者假定该方法仅从工作线程调用。因为唯一的好处是您将编写更少的代码,这是最糟糕的选择IncrementAndShowCounter
,因此使其适应情况可以让您继续处理其他问题。这是简单而好的;这也是编写库代码时所能做到的最好的方法(不允许任何假设)private void IncrementAndShowCounter()
{
i++;
myText.Text = i.ToString();
}
myButton.Click += (_, __) => IncrementAndShowCounter();
timer.Elapsed += (_, __) => Dispatcher.BeginInvoke(IncrementAndShowCounter);
如果100%确定调用线程是UI线程,那么可以直接使用“DO”方法
如果100%确定调用线程不是UI线程,但操作应该在UI线程上完成,则只需调用BeginInvoke
....
// 100% I'm sure the Click handler will be invoked on UI thread
myButton.Click += (_, __) => IncrementAndShowCounter();
// here I'm not sure
timer.Elapsed += (_, __) => Dispatcher.BeginInvoke(IncrementAndShowCounter);
// 100% not UI thred here:
Task.Factory.StartNew(() => Dispatcher.BeginInvoke(IncrementAndShowCounter), TaskScheduler.Default)
private void IncrementAndShowCounter()
{
i++;
myText.Text = i.ToString();
}
首先,没有太多的性能冲击。将委托添加到队列末尾将是一个非常快速的操作。此外,正如在另一个答案中提到的,您应该始终知道给定的代码块是从UI线程调用还是从非UI线程调用。您几乎不应该不知道某些代码是否正在UI线程中运行。几乎没有任何理由通过编程检查您是否在生产代码的UI线程中。@首先,我强烈建议任何人不要创建具有多个UI线程的应用程序。对于任何给定的问题,它几乎总是不恰当的解决方案。相反,应该使用单个UI线程。也就是说,即使有人处于您描述的情况下,他们仍然知道自己没有处于正确的线程中,并且需要调用另一个调度程序。不需要有条件地进行检查。@lll如果不能在UI线程外执行,则应异步执行。创建长时间阻止UI线程并防止其发送消息的代码几乎肯定是一个设计不当的解决方案,应该解决这个问题,而不是通过第二个UI线程。但是,正如我前面所说的,即使你选择使用第二个UI线程,你仍然知道你与你想要使用的线程处于不同的线程中,并且不需要通过编程进行检查。@Jon one几乎永远不会处于不知道代码是否在UI线程中运行的情况。设计良好的程序可以防止这种情况发生。在您真正不确定的异常情况下,总是调用而不检查实际上没有任何问题。你所描述的性能打击将是完全可以忽略不计的,特别是考虑到这是一种几乎永远不会发生的情况。这是对微优化的过早优化。@Jon问:“为什么不总是使用BeginInvoke并跳过CheckAccess?”回答:“因为与直接运行代码相比,使用BeginInvoke会降低性能。”在什么情况下,这并不是说性能是您在调用之前应该调用
CheckAccess
的原因?我根本不认为性能是一个考虑因素,性能差异只是会很小。也许开发人员认为应该有一个性能的冲击,而实际上没有,这就是为什么,但我看不出这实际上是一个有效的优化。