Dispatcher.Invoke from a new thread is locking my UI
Asked Answered
E

4

3

i'm using wpf, there's a button on my ui.

when the user clicks it, i have a for loop that runs a new method, on a new thread using autoresetevent.

in that method on that new thread, i'm using a label, let's call it lblStatus. i want to update that label on this thread that's not on the ui. using wpf, i have to use Dispatcher.Invoke.

here's a sample of my code:

 Thread thread= new Thread(StartLooking);
 thread.Start();
 _waitHandle.WaitOne();

 private void StartLooking(object value)
{
 if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
        {
            lblStatus.Content = "Scanning>...";
        }
        else
        {
            lblStatus.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => lblStatus.Content = "Scanning>>>>>")); 
        }
 _waitHandle.Set();
}

the program just stops here. it doesn't change the content of the label, it returns to my ui, but blocks it.
i've tried

lblStatus.Dispatcher.Invoke(DispatcherPriority.Normal, new LblStatusThreadCheck(lblStatusThreadCheck), "Scanning...");

as well, but that isn't working also. any ideas?

Erv answered 15/12, 2011 at 22:55 Comment(2)
Have you thought of using data binding?Cato
Why do you start a new thread if you want to wait for it to complete the operation before anything can be done?Gavan
L
9

The problem is that you're making it impossible for this to execute, since you're using Invoke.

Dispatcher.Invoke will not return until the UI thread processes. However, you've blocked the UI thread by calling _waitHandle.WaitOne();, and don't set the wait handle until AFTER this processes. The two effectively cause a dead lock.

If you switch this to use BeginInvoke instead, the UI will queue the element, the wait handle will set, THEN the label will update. It will work and not block, however.

Landri answered 15/12, 2011 at 23:1 Comment(0)
H
3

Since the two previous posts already cover the problem in your code, just a suggestion: instead of

if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)

try using

if (!lblStatus.CheckAccess())

It's cleaner and has the exact intent you want. Just read about it here.

Herzel answered 15/12, 2011 at 23:5 Comment(0)
C
0

You probably want to use BeginInvoke instead. Invoke will block the thread that called it until the UI thread has run the Action, and since you're setting the priority to Background, this could take some time.

Chindwin answered 15/12, 2011 at 22:58 Comment(0)
S
0

Best solution I have found for .net 4.5+ is using SynchronizationContext Post

Example (Task.Run's can be as many as you want in parallel accessing UI):

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    var context = SynchronizationContext.Current;
    Task.Run(() =>
    {
        var i = 0;
        while (true)
        {
            context.Post((tmp) =>
            {
                uiText.Text = $"{i}";
            }), this);

            Thread.Sleep(1000);
            i++;

        }
    });

}
Sentience answered 4/4, 2018 at 11:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.