WPF Dispatcher.Invoke'hanging'

时间:2008-11-05 02:01:54

标签: c# .net wpf invoke dispatcher

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

一般过程是:

  1. 处理按钮上的点击事件
  2. 创建一个新线程(STA),其中:创建演示者和UI的新实例,然后调用方法断开连接
  3. 断开连接,然后在名为名称
  4. 的用户界面上设置属性
  5. Name的setter然后使用以下代码设置属性:
  6. 
        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.
    

    我的问题是,当调度调度程序调用时,它似乎每次都挂起,并且callstack指示它处于休眠状态,等待或加入Invoke实现。

    那么,有什么我做错了,我错过了,明显与否,或者是否有更好的方法调用UI线程来设置这个属性(以及其他)?

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

7 个答案:

答案 0 :(得分:10)

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

答案 1 :(得分:8)

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

线程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然后将排队另一个派遣项目。永远不会触发此项目,因为您的调度员永远不会完成处理您之前的请求;它已经卡住了。

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

答案 2 :(得分:5)

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

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

答案 3 :(得分:3)

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

我也在使用Invoke进行挂起,或者如果我可以在BeginInvoke中调用我的委托 - 似乎在做这一切: - (

答案 4 :(得分:2)

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

答案 5 :(得分:1)

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

 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更新。不幸的是,当我同步进行时,我遇到了死锁状态...

答案 6 :(得分:0)

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

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

我必须展示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--;
    }
}