WPF Dispatcher.Invoke 'hanging'

血红的双手。 提交于 2019-12-20 17:23:04

问题


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.

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:

    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 invoke method 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.

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)?

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.


回答1:


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

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.




回答2:


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




回答3:


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

Thread A does this:

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

Thread B does this:

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...

  • 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.

You now have a beautiful deadlock.




回答4:


I think you mean if (!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 :-(




回答5:


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.




回答6:


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...




回答7:


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.

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().

In my case I could just change a specific lock (locker) { ... } statement, and replace it with an 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--;
    }
}


来源:https://stackoverflow.com/questions/264163/wpf-dispatcher-invoke-hanging

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!