Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding the WPF Dispatcher.BeginInvoke

I was under the impression that the dispatcher will follow the priority of the operations queued it and execute the operations based on the priority or the order in which the operation was added to the queue(if same priority) until I was told that this is no the case in case of the WPF UI dispatcher.

I was told that if a operation on the UI thread takes longer duration say a database read the UI dispatcher simple tries to execute next set of operations in the queue. I could not come to terms with it so decided to write a sample WPF application which contains a button and three rectangles, on click of the button, the rectangles are filled with different colors.

<StackPanel>
    <Button x:Name="FillColors" Width="100" Height="100" 
            Content="Fill Colors" Click="OnFillColorsClick"/>
    <TextBlock Width="100" Text="{Binding Order}"/>
    <Rectangle x:Name="RectangleOne" Margin="5" Width="100" Height="100" Fill="{Binding BrushOne}" />
    <Rectangle x:Name="RectangleTwo" Margin="5" Width="100" Height="100" Fill="{Binding BrushTwo}"/>
    <Rectangle x:Name="RectangleThree" Margin="5" Width="100" Height="100" Fill="{Binding BrushThree}"/>
</StackPanel>

and in the code-behind

private void OnFillColorsClick(object sender, RoutedEventArgs e)
{
    var dispatcher = Application.Current.MainWindow.Dispatcher;

    dispatcher.BeginInvoke(new Action(() =>
    {
        //dispatcher.BeginInvoke(new Action(SetBrushOneColor), (DispatcherPriority)4);
        //dispatcher.BeginInvoke(new Action(SetBrushTwoColor), (DispatcherPriority)5);
        //dispatcher.BeginInvoke(new Action(SetBrushThreeColor), (DispatcherPriority)6);

        dispatcher.BeginInvoke(new Action(SetBrushOneColor));
        dispatcher.BeginInvoke(new Action(SetBrushTwoColor));
        dispatcher.BeginInvoke(new Action(SetBrushThreeColor));

    }), (DispatcherPriority)10);
}

private void SetBrushOneColor()
{
    Thread.Sleep(10 * 1000);
    Order = "One";
    //MessageBox.Show("One");
    BrushOne = Brushes.Red;
}

private void SetBrushTwoColor()
{
    Thread.Sleep(12 * 1000);
    Order = "Two";
    //MessageBox.Show("Two");
    BrushTwo = Brushes.Green;
}

private void SetBrushThreeColor()
{
    Thread.Sleep(15 * 1000);
    Order = "Three";
    //MessageBox.Show("Three");
    BrushThree = Brushes.Blue;
}

public string Order
{
    get { return _order; }
    set
    {
        _order += string.Format("{0}, ", value);
        RaisePropertyChanged("Order");
    }
}

The commented code works as expected the methods are invoked based on the DispatcherPriority and I also get to see the screen refresh after each operation has been completed. Order is One, Two, Three. Colors are drawn one after another.

Now the working code where the DispatcherPriority is not mentioned ( I assume it would default to Normal) the order is still One, Two, Three but if I show a MessageBox inside the methods, the
Thrid popup is show first then Two then One but when I debug I could see the methods are
invoked in the expected order (IntelliTrace even shows that a message box is shown but I don't see it on the screen at that time and see it only after the last operation is finished.) its just that the MessageBoxes are shown in the reverse order.

Is it because MessageBox.Show is a blocking call and the operation are cleared after the message has been closed.
Even then the order of the MessageBox should also be One, Two andThree` ?

like image 281
Vignesh.N Avatar asked Sep 22 '14 16:09

Vignesh.N


People also ask

What is dispatcher BeginInvoke?

BeginInvoke(DispatcherPriority, Delegate) Executes the specified delegate asynchronously at the specified priority on the thread the Dispatcher is associated with. BeginInvoke(Delegate, DispatcherPriority, Object[])

How does WPF dispatcher work?

WPF Dispatcher is associated with the UI thread. The UI thread queues methods call inside the Dispatcher object. Whenever your changes the screen or any event executes, or call a method in the code-behind all this happen in the UI thread and UI thread queue the called method into the Dispatcher queue.

What is the use of a dispatcher object in WPF?

This class provides a property named Dispatcher that returns the Dispatcher object associated with the WPF element. The Dispatcher class is used to perform work on its attached thread. It has a queue of work items and it is in charge of executing the work items on the dispatcher thread. Save this answer.

How many dispatchers are there in WPF?

A dispatcher is always associated with a thread and a thread can have at most one dispatcher running at the same time. A thread does not need to have a dispatcher. By default there is only one Dispatcher - For the UI.


1 Answers

Before coming down to your code behavior it's a prerequisite to understand the priorities of Dispatcher. DispatcherPriority is divided into ranges as shown in below image.

DispatcherPriority

If you simply queue 4 actions to 4 above ranges on Dispatcher. the Foreground queue will get executed first, then the Background and then in last Idle queue. priority 0 will not get executed.

Now your code:

Three task are queued 1st in background, 2nd in background and 3rd in foreground queue. So 3rd will get executed first. then 2nd task cause it has higher priority then 1st task. I hope that clears it.

Although some more observation will help you understand it better like, what if you have set the priorities as 7,8 and 9. So as this is a foreground queue, 7 will get executed first then 7 and then 8. One by one and exclusively in that order and while 7 is getting executed, 8 and 9 will wait, meaning foreground queue will get executed synchronously to each another.

But Background and Idle queue will not behave in that way the where execution is asynchronous to other tasks and tasks will follow the priority. And first Background and the Idle queue.

Hope this explanation clarifies to some extent.

like image 135
Kylo Ren Avatar answered Oct 05 '22 05:10

Kylo Ren