简体   繁体   English

为什么需要UI线程检查

[英]Why do I need a UI Thread check

To update the UI from an other thread you need to call the BeginInvoke method of the dispatcher. 要从其他线程更新UI,您需要调用调度程序的BeginInvoke方法。 Before you invoke you method you can check whether the calling thread is associated with the dispatcher. 在调用方法之前,可以检查调用线程是否与调度程序关联。

For my example I have 2 ways to update a textbox; 对于我的示例,我有两种更新文本框的方法; by clicking a button and by elapsing a timer. 通过单击按钮并设置计时器。 The Code: 编码:

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();
                }));
            }
        }
    }
}

When I don't CheckAccess() and just always execute the BeginInvoke everything works fine. 当我不使用CheckAccess()并始终执行BeginInvoke时,一切正常。

So my question is why not always use the BeginInvoke and skip the CheckAccess? 所以我的问题是为什么不总是使用BeginInvoke并跳过CheckAccess?

So my question is why not always use the BeginInvoke and skip the CheckAccess ? 所以我的问题是,为什么不总是使用BeginInvoke并跳过CheckAccess

That's exactly what you should do most of the time if invoking is required (ie you are touching a control owned by another thread). 需要调用的情况下 (即您正在触摸另一个线程拥有的控件),这就是大多数时候应该做的事情。 If invoking is not required then you should skip both of them. 如果不需要调用,则应将它们都跳过。

Using CheckAccess implies that your code doesn't know or doesn't want to assume that it will run on the "correct" thread. 使用CheckAccess意味着您的代码不知道或不想假设它将在“正确”线程上运行。 There are two main reasons for this: genericity (your code is in a library and you can't predict how it will be used) and convenience (you want only one method to take care of both cases, or you want the freedom to change the mode of operation without breaking the program). 造成这种情况的主要原因有两个:通用性(您的代码在库中,并且您无法预测其用法)和便利性(您只希望使用一种方法来处理这两种情况,或者希望自由更改)操作模式而不破坏程序)。

Your example falls in the second category: the same method services both modes of operation. 您的示例属于第二类:相同的方法可同时服务于两种操作模式。 In this case you have three possible options: 在这种情况下,您有三个可能的选择:

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

    This is going to give you a performance hit (a negligible one here), and it will also make readers of the code assume that the method is only called from worker threads. 这将给您带来性能上的损失(在这里可以忽略不计),并且还将使代码阅读器假定该方法仅从工作线程中调用。 Since the only benefit is that you will be writing a little less code, this is the worst option. 由于唯一的好处就是您将编写更少的代码,所以这是最糟糕的选择。

  2. Keep everything as it is. 保持一切不变。

    Since IncrementAndShowCounter is called from both UI and worker threads, making it adapt to the situation lets you move on to other problems. 由于从UI线程和辅助线程都调用了IncrementAndShowCounter ,因此使其适应情况可以使您继续处理其他问题。 This is simple and good; 这很简单,也很好。 it's also the best you can do when writing library code (no assumptions allowed). 这也是编写库代码时所做的最好的事情(不允许假设)。

  3. Never invoke from within the method, do it from outside as required. 切勿从方法内部调用,请根据需要从外部进行调用。

    This is the best option on technical merit: since you know the context in which the method will be called, arrange for the invocation to happen outside it. 这是技术上的最佳选择:由于您知道将在其中调用该方法的上下文,因此请安排在该方法之外进行调用。 This way the method is not tied to any specific thread and you don't get unnecessary performance penalties. 这样,该方法就不会绑定到任何特定的线程,并且您不会受到不必要的性能损失。

Here's example code for the third option: 这是第三个选项的示例代码:

private void IncrementAndShowCounter()
{
    i++;
    myText.Text = i.ToString();
}

myButton.Click += (_, __) => IncrementAndShowCounter();
timer.Elapsed += (_, __) => Dispatcher.BeginInvoke(IncrementAndShowCounter);

If you 100% sure that the calling thread is UI thread - you can use the "DO" method directly. 如果您100%确保调用线程是UI线程-您可以直接使用“ DO”方法。

If you 100% sure that the calling thread is not UI thread, but the operation should be done on the UI thread, you just call the BeginInvoke 如果您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