2

Instead of working in the background - this code still freeze my program:

private void button_Click(object sender, RoutedEventArgs e)
{
    this.Dispatcher.BeginInvoke(new Action(() =>
    {
        Thread.Sleep(5000);
        label.Content = "Done";
    }), DispatcherPriority.Normal);
}

I have tried with Thread/Tasks, thread example:

private void button_Click(object sender, RoutedEventArgs e)
{
    var t = new Thread(new ThreadStart(runtask));
    t.Start();
}

private void runtask()
{
    this.Dispatcher.BeginInvoke(new Action(() =>
    {
        Thread.Sleep(5000);
        label.Content = "Done";
    }), DispatcherPriority.Normal);
}

Task example:

private void button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(() =>
    {
        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
        {
            Thread.Sleep(5000);
            label.Content = "Done";
        }));
    });
}

And still my program is freezing. Any suggestions?

Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
Rhonin
  • 474
  • 4
  • 20

2 Answers2

7

From the documentation of the Dispatcher class:

Provides services for managing the queue of work items for a thread.

From the documentation of Dispatcher.BeginInvoke:

Executes the specified delegate asynchronously with the specified arguments on the thread that the Dispatcher was created on.

Here "asynchronously" refers to the secondary thread, not the main one. Because the main one is owned by the main Dispatcher. That means that every call of Invoke or BeginInvoke on that Dispatcher, from whatever Thread, will put the invoked Action in the queue of operations that the main Thread must execute, but from the point of view of the main Thread they will be executed synchronously, one after the other.

For example, if you put 3 Action like Thread.Sleep(1000); within 10 ms on the Dispatcher, whether with Invoke or BeginInvoke and from whether Thread, that Dispatcher will make the UI Thread to execute the 3 Action synchronously, so they will take a total of 3000 ms.

Maybe the documentation about BeginInvoke could have been written better, like:

Executes the specified delegate with the specified arguments on the thread that the Dispatcher was created on. The specified delegate is executed asynchronously from the point of view of the calling thread.

Now... Invoke or BeginInvoke?

Using Invoke, the secondary Thread is saying to the Dispatcher: let's execute this on the main Thread, and don't dare to return until your thread's job has finished. Then and only then I will continue.

For example, if you write this:

this.Dispatcher.Invoke(new Action(() =>
    {
        Thread.Sleep(5000);
        Debug.WriteLine("After Sleep");
    }));
Debug.WriteLine("Continuation on secondary Thread");

The Console will print after ~ 5000 ms:

"After Sleep"

"Continuation on secondary Thread"

Using BeginInvoke, instead, the Thread is saying: "hey, Dispatcher, queue this operation on the main Thread, but return as soon as possible so I can continue my job immediately".

In this case the Console will print immediately:

"Continuation on secondary Thread"

And after ~ 5000 ms:

"After Sleep"


Now, if your purpose is to execute some heavy operation on the background, you should learn about the async/await pattern, available from .NET 4.5 and C# 5.0.

In your example, I would write:

private async void button_Click(object sender, RoutedEventArgs e)
{
    await Task.Delay(5000); // await a heavy operation executed in background

    label.Content = "Done"; // control back to the UI Thread that executes this
}
Community
  • 1
  • 1
Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
  • Async / await sounds good, but need to use dispatcher inside code anwyay, if I want to change the UI during operation (like progress bar) – Rhonin Sep 17 '17 at 06:38
  • 2
    @Rhonin, that's where Bindings come out to help you. You have a View. The view is coupled with a ViewModel. The ViewModel has a property, `string LabelContent`, bound to the `Content` property of the `Label`. Then you can update that `LabelContent` from whatever Thread: the Binding will take care of dispatching it to the UI Thread, avoiding a CrossThreadException. WPF is so beautiful... – Massimiliano Kraus Sep 17 '17 at 10:49
-1

You can use this small extension if your UI access is the last of your method.

https://mitsufu.wordpress.com/2015/08/03/dispatcher-switchto/

private void Button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(async () =>
    {
        //heavy background operation
        await Dispatcher.SwitchTo();
        Title = "ok";
    });
}
Mitsuru Furuta
  • 131
  • 1
  • 6
  • This makes no sense at all. The Binding already handles the re-marshalling on the main thread (at least for simple properties), and the async/await pattern allows to write code that can continuously switch between threads very smoothly, and all these stuffs are already built-in inside WPF and C# 5.0. No need for external libraries. – Massimiliano Kraus Oct 18 '17 at 15:46
  • Hi Massimiliano, Yes WPF does it automatically throught binding but I'm not using any binding here :) (Rhonin does not either) Regarding the case of binding, you should be cautious about automatic synchronization. If you know you will update a bunch of properties you will switch contexts many times for nothing, explicit synchronization will be more effective. Moreover, thread synchronization is not only about UI blocking, many people use it to serialize calls to avoid conflicts. The web is full of articles about WPF explicit UI sync, even today. – Mitsuru Furuta Oct 18 '17 at 17:15
  • @MassimilianoKraus, interesting reading here about ui thread sync around different frameworks: https://github.com/lbugnion/mvvmlight/tree/master/Samples/DispatcherHelperSampleStd – Mitsuru Furuta Oct 25 '17 at 10:21
  • _"you will switch contexts many times for nothing, explicit synchronization will be more effective"_... I asked exactly that question [some time ago](https://stackoverflow.com/questions/40530737/many-awaits-for-async-method-or-a-single-await-for-a-wrapping-task-run). _"many people use it to serialize calls to avoid conflicts"_... yes, in those cases you're right. But imho it's not a so common case (also because usually you put a spinner on the UI, so the user cannot do other things in the middle). Thank you for the link, anyway, it's interesting. – Massimiliano Kraus Oct 26 '17 at 10:10