WPF Dispatcher.Invoke’hanging’

我有一个有点复杂的WPF应用程序,当尝试使用调度程序在UI线程上调用调用时,似乎是“挂起”或卡在Wait调用中。

一般过程是:

  1. 处理按钮上的单击事件
  2. 创建一个新线程(STA),其中:创建演示者和UI的新实例,然后调用Disconnect方法
  3. 断开然后在名为Name的UI上设置属性
  4. 然后,Name的setter使用以下代码来设置属性:
if(this.Dispatcher.Thread != Thread.CurrentThread) { this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{ this.Name = value; // Call same setter, but on the UI thread }); return; } SetValue(nameProperty, value); // I have also tried a member variable and setting the textbox.text property directly. 

我的问题是,当调用 dispatcher invoke方法时,它似乎每次都挂起,并且callstack指示它处于hibernate状态,等待或加入Invoke实现。

那么,有什么我做错了,我错过了,显而易见或没有,或者有更好的方法调用UI线程来设置这个属性(和其他)?

编辑:解决方案是在线程委托结束时调用System.Windows.Threading.Dispatcher.Run()(例如,正在执行工作的地方) – 感谢所有帮助过的人。

你说你正在创建一个新的STA线程,这个新线程的调度程序是否正在运行?

我是从“this.Dispatcher.Thread!= Thread.CurrentThread”得到的,你希望它是一个不同的调度员。 确保它运行,否则它不会处理它的队列。

调用是同步的 – 您需要Dispatcher.BeginInvoke。 另外,我相信你的代码示例应该在“else”语句中移动“SetValue”。

我认为这可以通过代码更好地显示出来。 考虑这种情况:

线程A执行此操作:

 lock (someObject) { // Do one thing. someDispatcher.Invoke(() => { // Do something else. } } 

线程B执行此操作:

 someDispatcher.Invoke(() => { lock (someObject) { // Do something. } } 

乍一看,一切看起来都很精致,但事实并非如此。 这将产生僵局。 调度程序就像一个线程的队列,当处理像这样的死锁时,以这种方式思考它们很重要:“以前的调度可能会阻塞我的队列?”。 线程A将进入…并在锁定下发送。 但是,如果线程B在标记为“做一件事”的代码中的线程A进入的时候会怎样? 好…

  • 线程A锁定someObject并运行一些代码。
  • 线程B现在调度,并且调度程序将尝试锁定someObject,从而干扰您的调度程序,因为线程A已经具有该锁定。
  • 然后,线程A将排队另一个调度项。 永远不会触发此项目,因为您的调度员永远不会完成处理您之前的请求; 它已经卡住了。

你现在有一个漂亮的僵局。

我想你的意思是(!this.Dispatcher.CheckAccess())

我也在使用Invoke进行挂起,或者如果我可以使用BeginInvoke我的委托没有被调用 – 似乎是在做这本书的所有内容:-(

这听起来像一个僵局; 如果调用.Invoke的线程已经拥有一个锁定/互斥/等,UI线程需要完成它的工作,这通常会发生。 最简单的方法是使用BeginInvoke:这样,当前线程可以继续运行,并且(可能)很快就会释放锁 – 允许UI获取它。 或者,如果您可以识别违规锁定,则可以故意将其释放一段时间。

我遇到了类似的问题,而我仍然不确定答案是什么,我认为你的问题

  if(this.Dispatcher.Thread != Thread.CurrentThread) { this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{ this.Name = value; // Call same setter, but on the UI thread }); return; } 

应该被替换

  if(this.Dispatcher.CheckAccess()) { this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{ this.Name = value; // Call same setter, but on the UI thread }); return; } 

CheckAccess不会出现在Intellisense中,但它就是为了这个目的而存在的。 此外,我同意一般你想在这里使用BeginInvoke,但是我发现当我执行此异步时我没有获得UI更新。 不幸的是,当我同步执行时,我遇到了死锁情况……

我知道这是一个旧线程,但这是另一种解决方案。

我刚刚解决了类似的问题。 我的调度员运行正常,所以……

我必须显示DEBUG – > THREAD WINDOW来识别在任何地方执行我的代码的所有线程。

通过检查每个线程,我很快看到哪个线程导致了死锁。

它是多个线程组合一个lock (locker) { ... }语句,并调用Dispatcher.Invoke()。

在我的情况下,我可以只更改一个特定的lock (locker) { ... }语句,并用Interlocked.Increment(ref lockCounter)替换它。

这解决了我的问题,因为避免了死锁。

 void SynchronizedMethodExample() { /* synchronize access to this method */ if (Interlocked.Increment(ref _lockCounter) != 1) { return; } try { ... } finally { _mandatoryCounter--; } }