[英]Why do I need a UI Thread check
要从其他线程更新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
情况下调用。
这将给您带来性能上的损失(在这里可以忽略不计),并且还将使代码阅读器假定该方法仅从工作线程中调用。 由于唯一的好处就是您将编写更少的代码,所以这是最糟糕的选择。
保持一切不变。
由于从UI线程和辅助线程都调用了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();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.