UI线程上的Dispatcher.Dispatch

我怀疑何时使用Dispatcher.Invoke从不同的线程更新UI上的内容。

这是我的代码……

public Window4() { InitializeComponent(); this.DataContext = this; Task.Factory.StartNew(() => Test() ); } private List listOfString = new List(); public List ListOfString { get { return listOfString; } set { listOfString = value; } } public void Test() { listOfString.Add("abc"); listOfString.Add("abc"); listOfString.Add("abc"); }    

我在不同的Thread上启动一个新的Task,我是否需要使用Dispatcher.BeginInvoke来更新UI。

在这种情况下,它正在更新UI,但我已经看到一些场景,人们使用来自不同线程的Dispatcher.Invoke或BeginInvoke更新UI。

所以我的问题是我们必须这样做,为什么在这种情况下它工作正常。

感谢和问候,BHavik

我怀疑何时使用Dispatcher.Invoke从不同的线程更新UI上的内容。

当您在不同的线程上时,您将始终必须使用调度程序来更新属于另一个线程的ui组件。

我在不同的Thread上启动一个新的Task,我是否需要使用Dispatcher.BeginInvoke来更新UI。

任务允许执行多个操作而不阻止调用它们的线程,但这并不意味着它们位于不同的线程上。 但是,从Task内部更新UI时,您需要使用调度程序。

在这种情况下,它正在更新UI,但我已经看到一些场景,人们使用来自不同线程的Dispatcher.Invoke或BeginInvoke更新UI。

调用将在执行操作时阻止调用线程,而BeginInvoke则不会。 BeginInvoke将立即将控制权返回给调用者,Invoke可能会导致调用线程在执行繁重操作时挂起。

这是来自msdn文档,

在WPF中,只有创建DispatcherObject的线程才能访问该对象。 例如,从主UI线程分离出来的后台线程无法更新在UI线程上创建的Button的内容。 为了让后台线程访问Button的Content属性,后台线程必须将工作委托给与UI线程关联的Dispatcher。 这是通过使用Invoke或BeginInvoke来完成的。 Invoke是同步的,Be​​ginInvoke是异步的。

编辑:为了回应您的评论,我进行了一些测试。

从任务调用Test()时(不使用调度程序)我收到此错误“调用线程无法访问此对象,因为另一个线程拥有它。”

所以我创建了一个名为PrintThreadID()的方法。 我在进入任务之前打印了线程,然后从任务内部打印出来,它确实报告两个都在相同的线程 ID上运行。

该错误具有误导性,因为它说调用线程与拥有它的那个不同,PrintThreadID()函数显示的不是真的,它们实际上在同一个线程上。 在不使用Dispather.Invoke()的情况下,同一线程上的任务仍无法更新UI组件。

所以这是一个工作示例,它将从任务更新Grid。


 public partial class MainWindow : Window { public List myList { get; private set; } public MainWindow() { InitializeComponent(); myList = new List(); label1.Content = Thread.CurrentThread.ManagedThreadId.ToString(); Task.Factory.StartNew(PrintThreadID); Task.Factory.StartNew(Test); } private void PrintThreadID() { label1.Dispatcher.Invoke(new Action(() => label1.Content += "..." + Thread.CurrentThread.ManagedThreadId.ToString())); } private void Test() { myList.Add("abc"); myList.Add("abc"); myList.Add("abc"); // if you do not use the dispatcher you will get the error "The calling thread cannot access this object because a different thread owns it." dataGrid1.Dispatcher.Invoke(new Action(() => { dataGrid1.ItemsSource = myList.Select(i => new { Item = i }); })); } } 

您的测试无效,因为它实际上并未更新您的UI。 如果您需要certificate,请添加此睡眠呼叫:

 public void Test() { Thread.Sleep(10000); listOfString.Add("abc"); listOfString.Add("abc"); listOfString.Add("abc"); } 

您会发现您的UI出现且列表为空。 10秒,30秒,3个月后,列表将不包含您的字符串。

相反,您的测试正在演示竞争条件 – 您的Test()方法正在以足够快的速度完成,以便在UI出现在屏幕上并读取列表之前将字符串添加到列表中。

要修复它,请将集合更改为ObservableCollection 。 但是接下来你会遇到下一个问题 – 你无法在后台线程上更新ObservableCollection 。 这就是Dispatcher用武之地。