WPF Dispatcher.Invoke 'hanging'



我有一个有点复杂的WPF应用程序,当试图使用调度器调用UI线程上的调用时,它似乎"挂起"或陷入等待调用。

一般流程为:

  1. 处理按钮上的单击事件
  2. 创建一个新线程(STA),该线程:创建演示者和UI的新实例,然后调用方法Disconnect
  3. Disconnect然后在UI上设置一个名为Name的属性
  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.

我的问题是,当调度程序invoke方法被调用时,它似乎每次都挂起,而调用堆栈指示它在invoke实现中处于睡眠、等待或联接状态。

那么,是不是我做错了什么,我遗漏了什么,无论是否明显,还是有更好的方法调用UI线程来设置这个属性(以及其他属性)?

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

Invoke是同步的-您需要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位于标记为"Do one thing"的代码中的时间点出现,该怎么办?好

  • 线程A锁定了someObject,并且正在运行一些代码
  • 线程B现在进行调度,调度程序将尝试获取someObject的锁,由于线程A已经有了锁,因此会阻塞您的调度程序
  • 然后,线程A将对另一个调度项目进行排队。此项目永远不会被激发,因为您的调度程序永远不会完成对您以前请求的处理;它已经堵塞了

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

您说您正在创建一个新的STA线程,这个新线程上的调度器正在运行吗?

我从"this.Dispatcher.Thread!=Thread.CurrentThread"中了解到,您希望它是一个不同的调度器。请确保它正在运行,否则它将不会处理其队列。

我想你的意思是如果(!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窗口来识别在任何地方执行我的代码的所有线程。

通过检查每个线程,我很快就发现是哪个线程导致了死锁。

它是多个线程组合一个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--;
    }
}

相关内容

  • 没有找到相关文章

最新更新