繁体   English   中英

为什么需要UI线程检查

[英]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意味着您的代码不知道或不想假设它将在“正确”线程上运行。 造成这种情况的主要原因有两个:通用性(您的代码在库中,并且您无法预测其用法)和便利性(您只希望使用一种方法来处理这两种情况,或者希望自由更改)操作模式而不破坏程序)。

您的示例属于第二类:相同的方法可同时服务于两种操作模式。 在这种情况下,您有三个可能的选择:

  1. 始终在没有CheckAccess情况下调用。

    这将给您带来性能上的损失(在这里可以忽略不计),并且还将使代码阅读器假定该方法仅从工作线程中调用。 由于唯一的好处就是您将编写更少的代码,所以这是最糟糕的选择。

  2. 保持一切不变。

    由于从UI线程和辅助线程都调用了IncrementAndShowCounter ,因此使其适应情况可以使您继续处理其他问题。 这很简单,也很好。 这也是编写库代码时所做的最好的事情(不允许假设)。

  3. 切勿从方法内部调用,请根据需要从外部进行调用。

    这是技术上的最佳选择:由于您知道将在其中调用该方法的上下文,因此请安排在该方法之外进行调用。 这样,该方法就不会绑定到任何特定的线程,并且您不会受到不必要的性能损失。

这是第三个选项的示例代码:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM