22

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.

Matthew Savage
  • 3,794
  • 10
  • 43
  • 53
  • @Matthew - actually, there is nothing "non-optimal" about BeginInvoke; if you don't absolutely need an update *now*, it is fine. You need to be slightly careful about captured variables, though (i.e. don't change "value" after calling BeginInvoke. At all.) – Marc Gravell Nov 07 '08 at 04:57
  • @Matthew - you don't Join() the new thread at all, do you? That would explain it... – Marc Gravell Nov 07 '08 at 04:58
  • 1
    @Marc Gravell - from memory I was joining the thread at some point, but I'm not sure if the behavior was the same when I wasn't using that. The reason for the join is I wanted to block the rest of the app until the work has completed, but maybe I can use an alternative. – Matthew Savage Nov 10 '08 at 02:28

7 Answers7

11

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

Ana Betts
  • 73,868
  • 16
  • 141
  • 209
8

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.

Alexandru
  • 12,264
  • 17
  • 113
  • 208
  • Thanks for the good explanation. Saved me hours of work. I fixed it by not acquiring locks in dispatcher calls (what your thread B does). Is there another solution for this problem? – Heribert Jun 05 '15 at 14:23
  • @Heribert It depends on the code you're working with. Deadlocks like these are very application-specific. If you are facing a case similar to the above, you can try to lock outside of dispatcher calls. – Alexandru Jun 05 '15 at 14:47
  • that's what I did :) Thanks again for the post and reply – Heribert Jun 05 '15 at 17:55
6

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.

Keith
  • 785
  • 1
  • 8
  • 20
  • Keith, This is a good point. I'm not familiar enough with the dispatcher, but wouldn't the window's dispatcher already be started? The STA Thread is used to create the new window, however if I need to start the dispatcher manually it would explain why it isn't processing... – Matthew Savage Nov 10 '08 at 23:24
  • If creating the STA yourself try calling Dispatcher.Run() after you show your window. My understanding is the dispatcher is a message pump and if creating a new UI thread it’ll have a dispatcher create when requested, if you managing the creation you’d have to call Run on the dispatcher. – Keith Nov 11 '08 at 09:20
  • 1
    Have a look at this post: http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/ – Keith Nov 11 '08 at 09:21
3

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 :-(

Andrew
  • 31
  • 1
2

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks Marc, This explanation is good, however I'm still left clueless as to why there is a lock in the first place. As suggested by yourself and Paul BeginInvoke was an option but not optimal, there is no guarantee of it completing. Crazy strange bugs.... – Matthew Savage Nov 07 '08 at 00:12
2

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

Jeff
  • 2,701
  • 2
  • 22
  • 35
0

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--;
    }
}
Martin Lottering
  • 1,624
  • 19
  • 31