繁体   English   中英

WPF Dispatcher.Invoke'hanging'

[英]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指示它处于休眠状态,等待或在Invoke实现中加入。

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

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

调用是同步的 - 您需要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现在调度,并且调度程序将尝试获取某个对象的锁定,从而干扰您的调度程序,因为线程A已经具有该锁定。
  • 然后,线程A将排队另一个调度项。 永远不会触发此项目,因为您的调度员永远不会完成处理您之前的请求; 它已经卡住了。

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

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

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

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

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

这听起来像一个僵局; 如果调用.Invoke的线程已经拥有一个锁定/互斥锁/ etc,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--;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM