C# WPF Dispatcher.Invoke '挂'

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/264163/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 20:26:32  来源:igfitidea点击:

WPF Dispatcher.Invoke 'hanging'

c#.netwpfinvokedispatcher

提问by Matthew Savage

I have a somewhat complex WPF application which seems to be 'hanging' or getting stuck in a Wait call when trying to use the dispatcher to invoke a call on the UI thread.

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

The general process is:

一般流程是:

  1. Handle the click event on a button
  2. Create a new thread (STA) which: creates a new instance of the presenter and UI, then calls the method Disconnect
  3. Disconnect then sets a property on the UI called Name
  4. The setter for Name then uses the following code to set the property:
  1. 处理按钮上的点击事件
  2. 创建一个新线程 (STA),它:创建演示者和 UI 的新实例,然后调用Disconnect方法
  3. 断开连接然后在 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.

My problem is that when the dispatcher invokemethod is called it seems to hang every single time, and the callstack indicates that its in a sleep, wait or join within the Invoke implementation.

我的问题是,当调用调度程序invoke方法时,它似乎每次都挂起,并且调用堆栈表明它在 Invoke 实现中处于睡眠、等待或加入状态。

So, is there something I am doing wrong which I am missing, obvious or not, or is there a better way of calling across to the UI thread to set this property (and others)?

那么,是否有什么我做错的地方我遗漏了,明显与否,或者是否有更好的方法调用 UI 线程来设置此属性(和其他属性)?

Edit:The solution was to call System.Windows.Threading.Dispatcher.Run() at the end of the thread delegate (e.g. where the work was being performed) - Thanks to all who helped.

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

采纳答案by Keith

You say you are creating a new STA thread, is the dispatcher on this new thread running?

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

I'm getting from "this.Dispatcher.Thread != Thread.CurrentThread" that you expect it to be a different dispatcher. Make sure that its running otherwise it wont process its queue.

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

回答by Ana Betts

Invoke is synchronous - you want Dispatcher.BeginInvoke. Also, I believe your code sample should move the "SetValue" inside an "else" statement.

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

回答by Marc Gravell

This sounds like a deadlock; this would typically happen if the thread calling .Invoke already held a lock / mutex / etc which the UI thread needs to complete it's work. The simplest approach would be to use BeginInvoke instead: that way, the current thread can keep running, and will (presumably) release the lock shortly - allowing the UI to aquire it. Alternatively, if you can identify the offending lock, you could deliberately release it for a duration.

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

回答by Jeff

I'm having a similar problem and while I'm still not sure what the answer is, I think your

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

 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;
}

should be replaced by

应该替换为

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

CheckAccess won't show up in Intellisense but it is there and meant for this purpose. Also, I agree that in general you want BeginInvoke here, however I've found that I don't get UI updates when I do this async. Unfortunately, when I do it synchronously I get a deadlock condition...

CheckAccess 不会出现在 Intellisense 中,但它在那里并且用于此目的。另外,我同意通常您希望在这里使用 BeginInvoke,但是我发现当我执行此异步操作时,我没有获得 UI 更新。不幸的是,当我同步执行时,我遇到了死锁情况...

回答by Andrew

I think you mean if (!this.Dispatcher.CheckAccess())

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

I am also geting a hang with Invoke, or if I can BeginInvoke my delegate isn't being called - seem to be doing everything by the book :-(

我也遇到了 Invoke,或者如果我可以 BeginInvoke,我的代表就不会被调用 - 似乎一切都在按照本书做:-(

回答by Martin Lottering

I know this is an old thread, but here is another solution.

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

I just fixed a similar problem. My dispatcher was running fine, so...

我刚刚修复了一个类似的问题。我的调度员运行良好,所以...

I had to show the DEBUG -> THREAD WINDOW to identify all the threads that are executing my code anywhere.

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

By checking each of the threads, I quickly saw which thread caused the deadlock.

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

It was multiple threads combining a lock (locker) { ... }statement, and calls to Dispatcher.Invoke().

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

In my case I could just change a specific lock (locker) { ... }statement, and replace it with an Interlocked.Increment(ref lockCounter).

就我而言,我可以更改特定lock (locker) { ... }语句,并将其替换为Interlocked.Increment(ref lockCounter).

That solved my problem because the deadlock was avoided.

这解决了我的问题,因为避免了僵局。

void SynchronizedMethodExample() {

    /* synchronize access to this method */
    if (Interlocked.Increment(ref _lockCounter) != 1) { return; }

    try {
    ...
    }
    finally {
        _mandatoryCounter--;
    }
}

回答by Alexandru

I think this is better shown with code. Consider this scenario:

我认为这最好用代码来展示。考虑这个场景:

Thread A does this:

线程 A 这样做:

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

Thread B does this:

线程 B 这样做:

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

Everything might appear fine and dandy at first glance, but its not. This will produce a deadlock. Dispatchers are like queues for a thread, and when dealing with deadlocks like these its important to think of them that way: "What previous dispatch could have jammed my queue?". Thread A will come in...and dispatch under a lock. But, what if thread B comes in at the point in time at which Thread A is in the code marked "Do one thing"? Well...

乍一看,一切似乎都很好,很花哨,但事实并非如此。这会产生死锁。Dispatchers 就像一个线程的队列,在处理像这样的死锁时,重要的是要这样想:“以前的什么调度会阻塞我的队列?”。线程 A 将进入......并在锁下调度。但是,如果线程 B 在线程 A 在标记为“做一件事”的代码中的时间点进入呢?好...

  • Thread A has the lock on someObject and is running some code.
  • Thread B now dispatches, and the dispatcher will try to get the lock on someObject, jamming up your dispatcher since Thread A has that lock already.
  • Thread A will then queue up another dispatch item. This item will never be fired, because your dispatcher will never finish processing your previous request; its already jammed up.
  • 线程 A 锁定了 someObject 并且正在运行一些代码。
  • 线程 B 现在调度,调度器将尝试获取 someObject 上的锁,因为线程 A 已经拥有该锁,所以会阻塞您的调度器。
  • 然后线程 A 将排队另一个调度项。这个项目永远不会被触发,因为你的调度员永远不会完成处理你之前的请求;它已经卡住了。

You now have a beautiful deadlock.

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