简体   繁体   English

了解WPF Dispatcher.BeginInvoke

[英]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 . 我的印象是dispatcher将遵循排队的操作的优先级,并根据优先级或操作添加到队列的顺序(如果相同的优先级)执行操作,直到我被告知这不是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. 有人告诉我,如果UI线程上的操作持续时间较长,则表示数据库读取UI调度程序只是尝试执行队列中的下一组操作。 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. 我无法接受它,因此决定编写一个示例WPF应用程序,其中包含一个按钮和三个矩形,点击按钮,矩形填充不同的颜色。

<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. 注释代码按预期工作,基于DispatcherPriority调用方法,并且我还可以在每个操作完成后看到屏幕刷新。 Order is One, Two, Three . OrderOne, 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 现在没有提到DispatcherPriority的工作代码(我假设它默认为Normal )顺序仍然是One, Two, Three但是如果我在方法中显示一个MessageBox
Thrid popup is show first then Two then One but when I debug I could see the methods are Thrid popup首先显示然后是Two然后是One但是当我调试时我可以看到方法
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 MessageBox es are shown in the reverse order. 在预期的顺序调用(甚至智能跟踪显示一个消息框显示,但我没有看到它在屏幕上,当时看到它最后一次操作完成之后。)它只是在MessageBox ES在所示相反的顺序。

Is it because MessageBox.Show is a blocking call and the operation are cleared after the message has been closed. 是因为MessageBox.Show是一个阻塞调用,并且在消息关闭后清除了操作。
Even then the order of the MessageBox should also be One , Two and Three` ? 即便如此, MessageBox的顺序也应该是OneTwo and Three`?

Before coming down to your code behavior it's a prerequisite to understand the priorities of Dispatcher . 在介绍代码行为之前,先了解Dispatcher的优先级。 DispatcherPriority is divided into ranges as shown in below image. DispatcherPriority分为范围,如下图所示。

的DispatcherPriority

If you simply queue 4 actions to 4 above ranges on Dispatcher . 如果您只是在Dispatcher上将4个操作排队到4个以上范围。 the Foreground queue will get executed first, then the Background and then in last Idle queue. Foreground队列首先执行,然后执行Background ,然后执行最后一个Idle队列。 priority 0 will not get executed. 优先级0不会被执行。

Now your code: 现在你的代码:

Three task are queued 1st in background , 2nd in background and 3rd in foreground queue. 三个任务中排队第一个background ,在第二background中,三foreground队列。 So 3rd will get executed first. 所以第3次将首先被执行。 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. 虽然更多的观察将帮助您更好地理解它,如果您将优先级设置为7,8和9,那么如果这是一个前台队列,则7将先执行然后执行7然后执行8.然后逐个执行按此顺序,当7执行时,8和9将等待,这意味着foreground队列将彼此同步执行。

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. 但是BackgroundIdle队列不会以这种方式运行,其中执行与其他任务异步,任务将遵循优先级。 And first Background and the Idle queue. 第一个BackgroundIdle队列。

Hope this explanation clarifies to some extent. 希望这个解释在某种程度上得到澄清。

This is because the first MessageBox is blocking the UI thread . 这是因为第一个MessageBox阻止了UI线程

What Dispatcher.BeginInvoke() is doing under the hood is taking your delegate and scheduling it to be run on the main UI thread during it's next idle period. Dispatcher.BeginInvoke()正在做什么是接受你的委托并安排它在下一个空闲时段期间在主UI线程上运行。 However, MessageBox will block whichever thread it is called from until it is closed. 但是, MessageBox将阻止它被调用的任何线程,直到它被关闭。 This means that the second MessageBox cannot be displayed until the first is cleared because the UI thread scheduler sees that the thread is already in use (waiting for the first MessageBox to be cleared) and can't execute the next delegate containing the second MessageBox . 这意味着第二个MessageBox在第一个被清除之前无法显示,因为UI线程调度程序发现该线程已被使用(等待第一个MessageBox被清除)并且无法执行包含第二个MessageBox的下一个委托。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM