了解WPF Dispatcher.BeginInvoke

Vig*_*h.N 16 c# wpf multithreading dispatcher

我的印象是,dispatcher将遵循排队的操作的优先级,并根据优先级或操作添加到队列的顺序(如果相同的优先级)执行操作,直到我被告知这不是案件的情况WPF UI dispatcher.

有人告诉我,如果UI线程上的操作持续时间较长,则表示数据库读取UI调度程序只是尝试执行队列中的下一组操作.我无法接受它,因此决定编写一个示例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>
Run Code Online (Sandbox Code Playgroud)

并在代码隐藏

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

注释代码按预期工作,基于此方法调用方法,DispatcherPriority 并且我还可以在每个操作完成后看到屏幕刷新. OrderOne, Two, Three.颜色是一个接一个地绘制的.

现在DispatcherPriority没有提到的工作代码(我假设它会默认Normal)顺序仍然是One, Two, Three但如果我MessageBox在方法内部显示,
Thrid弹出窗口先显示然后Two然后One当我调试时我可以看到方法被
调用预期的顺序(IntelliTrace甚至显示一个消息框显示但我当时没有在屏幕上看到它并且只在最后一次操作完成后才看到它.)它只是以MessageBox相反的顺序显示.

是因为MessageBox.Show是阻塞调用,并且在消息关闭后清除操作.
即使这样,顺序MessageBox也应该是One,Two and三个?

Kyl*_*Ren 7

在讨论代码行为之前,先决条件是了解Dispatcher. DispatcherPriority分为如下图所示的范围。

调度程序优先级

如果您只是将 4 个操作排队到 4 个以上范围Dispatcher。队列Foreground将首先被执行,然后Background是最后一个Idle队列。优先级0不会被执行。

现在你的代码:

三个任务在队列中排在第一个background、第二个background和第三个foreground。所以第 3 个将首先被执行。然后第二个任务因为它的优先级高于第一个任务。我希望这能清除它。

虽然更多的观察将帮助您更好地理解它,例如,如果您将优先级设置为 7,8 和 9,会怎么样。因此,由于这是一个前台队列,因此 7 将首先执行,然后是 7,然后是 8。一个接一个且排他地执行按照这个顺序,当 7 被执行时,8 和 9 将等待,这意味着foreground queue will get executed synchronously to each another.

但是Background队列Idle不会以这种方式运行,即执行与其他任务异步,并且任务将遵循优先级。首先BackgroundIdle队列。

希望这个解释能在一定程度上澄清。


Ada*_*son 5

这是因为第一个MessageBox阻塞了UI 线程

Dispatcher.BeginInvoke()幕后所做的就是获取您的委托并安排它在下一个空闲期间在主 UI 线程上运行。但是,MessageBox将阻塞调用它的任何线程,直到它关闭为止。这意味着MessageBox在第一个被清除之前无法显示第二个,因为 UI 线程调度程序发现该线程已在使用中(等待第一个MessageBox被清除)并且无法执行包含第二个的下一个委托MessageBox