web-dev-qa-db-ja.com

WPF Dispatcher.BeginInvokeを理解する

dispatcherは、キューに入れられた操作の優先順位に従い、優先順位または操作がキューに追加された順序(同じ優先順位の場合)に基づいて、これがそうでないと言われるまで操作を実行するという印象を受けましたWPF UI dispatcherの場合。

UIスレッドでの操作に時間がかかる場合、データベースがUIディスパッチャーを読み取ると、キュー内の次の一連の操作を実行しようとする、と言われました。私はそれに同意することができなかったので、ボタンと3つの長方形を含むサンプル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>

そして、分離コードで

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");
    }
}

コメントされたコードは期待どおりに機能し、メソッドはDispatcherPriorityに基づいて呼び出され、各操作が完了した後に画面が更新されることもわかります。 OrderOne, Two, Threeです。色は次々に描かれます。

現在、DispatcherPriorityが記載されていない作業コード(デフォルトではNormalであると仮定しています)の順序はまだOne, Two, Threeですが、メソッド内にMessageBoxを表示すると、
Thridポップアップが最初に表示され、次にTwoが表示され、次にOneが表示されますが、デバッグするとメソッドが表示されます
期待される順序で呼び出されます(IntelliTraceはメッセージボックスが表示されていることを示していますが、その時点では画面に表示されず、最後の操作が終了した後にのみ表示されます)。それはMessageBoxes逆順で表示されます。

MessageBox.Showはブロッキング呼び出しであり、メッセージが閉じられた後に操作がクリアされるためです。
それでも、MessageBoxの順序もOneTwo andThree`でなければなりませんか?

15
Vignesh.N

コードの動作を理解する前に、Dispatcherの優先順位を理解することが前提条件です。 DispatcherPriorityは、下の画像に示すように範囲に分割されます。

DispatcherPriority

Dispatcherで上記の4つの範囲に4つのアクションを単純にキューイングする場合。 Foregroundキューが最初に実行され、次にBackgroundが実行され、次に最後のIdleキューが実行されます。優先度0は実行されません。

今あなたのコード:

3つのタスクは、backgroundで1番目、backgroundで2番目、foregroundキューで3番目にキューに入れられます。したがって、3番目が最初に実行されます。次に、2番目のタスクが1番目のタスクよりも高い優先度を持ちます。これで解決することを願っています。

優先順位を7,8および9に設定した場合はどうでしょうか。たとえば、これがフォアグラウンドキューであるため、7が最初に実行され、次に7、次に8が実行されます。その順序で、7が実行されている間、8と9は待機します。つまり、foregroundキューは互いに同期して実行されます。

ただし、BackgroundおよびIdleキューは、実行が他のタスクと非同期であり、タスクが優先順位に従っているような動作をしません。そして最初のBackgroundIdleキュー。

この説明がある程度明確になることを願っています。

4
Kylo Ren

これは、最初のMessageBoxIスレッドをブロックしているためです。

Dispatcher.BeginInvoke()が内部で行うことは、デリゲートを取得し、次のアイドル期間中にメインUIスレッドで実行されるようにデリゲートをスケジュールすることです。ただし、MessageBoxは、閉じられるまで呼び出し元のスレッドをブロックします。つまり、2番目のMessageBoxは、最初のスレッドがクリアされるまで表示できないことを意味します。UIスレッドスケジューラは、スレッドが既に使用されていることを確認し(最初のMessageBoxがクリアされるのを待っている) t 2番目のMessageBoxを含む次のデリゲートを実行します。

2
Adam Wilson