为什么我需要一个UI线程检查
本文关键字:一个 UI 线程 检查 为什么 | 更新日期: 2023-09-27 18:02:15
要从其他线程更新UI,需要调用分派器的BeginInvoke方法。在调用方法之前,您可以检查调用线程是否与调度程序相关联。
对于我的例子,我有两种方法来更新文本框;通过点击一个按钮并运行一个计时器。代码:
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();
}));
}
}
}
}
当我不CheckAccess(),只是总是执行BeginInvoke一切工作正常。
所以我的问题是为什么不总是使用BeginInvoke和跳过CheckAccess?
所以我的问题是为什么不总是使用
BeginInvoke
而跳过CheckAccess
?
这正是你应该做的大部分时间,如果调用是必需的(即你正在触摸由另一个线程拥有的控件)。如果不需要调用,则应该跳过它们。
使用CheckAccess
意味着你的代码不知道或不想假设它将在"正确"的线程上运行。这样做有两个主要原因:泛型(你的代码在一个库中,你不能预测它将如何被使用)和便利性(你只需要一个方法来处理这两种情况,或者你想在不破坏程序的情况下自由地改变操作模式)。
您的示例属于第二类:相同的方法服务于两种操作模式。在本例中,您有三个可能的选项:
-
始终不调用
CheckAccess
这将给您带来性能上的影响(在这里可以忽略不计),并且还会使代码的读者认为该方法只能从工作线程调用。由于唯一的好处是您将编写更少的代码,因此这是最糟糕的选择。
-
使一切。
由于
IncrementAndShowCounter
是由UI和工作线程调用的,使其适应这种情况可以让您继续处理其他问题。这既简单又好;这也是你在编写库代码时所能做的最好的事情(不允许有任何假设)。 -
永远不要从方法内部调用,根据需要从外部调用
从技术上来说,这是最好的选择:因为您知道将在其中调用方法的上下文,所以可以安排调用在上下文之外进行。这样,该方法就不会绑定到任何特定的线程,并且您不会得到不必要的性能损失。
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();
}