簡體   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