2014-09-22 8 views
15

Verständnis war ich unter dem Eindruck, dass die dispatcher der Priorität der Operationen es die Warteschlange folgen und die Operationen basierend auf der Priorität oder die Reihenfolge, in der die Operation ausgeführt zu der Warteschlange hinzugefügt wurde (wenn gleiche Priorität) bis mir gesagt wurde, dass dies im Fall der WPF UI dispatcher nicht der Fall ist.die WPF Dispatcher.BeginInvoke

Ich wurde gesagt, dass, wenn eine Operation auf dem UI-Thread längere Dauer dauert sagen eine Datenbank lesen der UI Dispatcher einfach versucht, nächste Reihe von Operationen in der Warteschlange auszuführen. Ich konnte mich nicht damit abfinden, also entschied ich mich, eine Beispiel-WPF-Anwendung zu schreiben, die eine Schaltfläche und drei Rechtecke enthält. Auf einen Klick werden die Rechtecke mit verschiedenen Farben gefüllt.

<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> 

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

Die kommentierten Code funktioniert wie erwartet die Methoden aufgerufen werden basierend auf dem DispatcherPriority und ich erhalte auch die Aktualisierung des Bildschirms zu sehen, nachdem jeder Vorgang abgeschlossen wurde. Order ist One, Two, Three. Farben werden nacheinander gezeichnet.

Nun wird der Arbeits Code, in dem die DispatcherPriority nicht erwähnt wird (ich nehme an, es zu Normal Standard würde) die Reihenfolge noch One, Two, Three ist, aber wenn ich ein MessageBox in den Methoden zeigen, der
Thrid Popup zeigt zunächst dann Two dann One aber wenn ich debugge, könnte ich sehen, dass die Methoden
in der erwarteten Reihenfolge aufgerufen werden (IntelliTrace zeigt sogar, dass ein Meldungsfeld angezeigt wird, aber ich sehe es nicht auf dem Bildschirm zu dieser Zeit und sehe es erst nach dem letzten Vorgang abgeschlossen ist.) Es ist nur, dass die MessageBox es in umgekehrter Reihenfolge angezeigt werden.

Liegt daran, dass MessageBox.Show ein blockierender Anruf ist und die Operation gelöscht wird, nachdem die Nachricht geschlossen wurde.
Auch dann sollte die Reihenfolge der MessageBox auch One, Two and Drei sein?

+0

Dies wird nur von der Reihenfolge beeinflusst, in der Sie die Meldungsfelder schließen. Da der letzte oben ist, ist das der erste, den du schließe. Hat nichts mit DispatcherPriority zu tun. –

+0

Ja..Ich dachte, aber ich bin nicht in der Lage, die anderen 'MessageBox's zu sehen, bevor ich den obersten schließe. –

+0

@ Vignesh.N erklärt meine Antwort Ihre Frage? –

Antwort

1

Dies ist, weil die erste MessageBox die UI-Thread blockiert.

Was unter der Haube Dispatcher.BeginInvoke() tut, nimmt Ihren Delegaten und planen, es auf dem Haupt-UI-Thread während seiner nächsten Leerlaufperiode ausgeführt werden. MessageBox blockiert jedoch den Thread, von dem es aufgerufen wird, bis es geschlossen wird. Dies bedeutet, dass die zweite MessageBox nicht angezeigt werden kann, bis die erste gelöscht wird, weil der UI-Thread-Scheduler erkennt, dass der Thread bereits verwendet wird (Warten auf die erste MessageBox wird gelöscht) und kann den nächsten Delegaten mit der zweiten MessageBox nicht ausführen.

+0

Wenn Sie Recht hatten, würde ich erwarten, dass die Nachrichten in der Reihenfolge angezeigt werden, in der sie aufgerufen wurden. Daher 1, 2, 3 nicht 3, 2 1 ... – blueprint

+0

Tatsächlich kann das Ausführungsreihenfolgeproblem hier jedoch höchstwahrscheinlich auf die verschachtelten BeginInvokes zurückgeführt werden. Der äußere BeginInvoke stellt bereits sicher, dass der Code auf der Benutzeroberfläche ausgeführt wird. Das einfache Entfernen der inneren BeginInvokes würde ausreichen, um sicherzustellen, dass der Code der Reihe nach ausgeführt wird. Das Aufrufen von BeginInvoke aus dem Benutzeroberflächenthread kann leicht zu unerwartetem Verhalten führen, da Sie sich bereits im Benutzeroberflächenthread befinden. –

+0

Sie haben absolut Recht, ich habe es selbst versucht. Ich habe auch ein paar Mal versucht, wirklich zu verstehen, was hinter den Kulissen in diesem Fall vor sich geht, aber wie auch immer ich es versuche, kam ich zum selben Schluss: Es sollte 1, 2, 3 sein. Kannst du das genauer erklären Was ist dein Modell für das Innenleben hier? Was passiert genau? – blueprint

4

Bevor Sie sich mit Ihrem Code-Verhalten befassen, ist es eine Grundvoraussetzung, um die Prioritäten von zu verstehen. DispatcherPriority ist in Bereiche unterteilt, wie in der Abbildung unten gezeigt.

DispatcherPriority

Wenn Sie einfach 4 Aktionen Warteschlange oben bis 4 Bereiche auf Dispatcher. Die Warteschlange Foreground wird zuerst ausgeführt, dann die Background und dann die letzte Idle Warteschlange.Priorität 0 wird nicht ausgeführt.

Jetzt Ihr Code:

Drei Aufgabe ist erst in background, 2. in background und 3. in foreground Warteschlange. Also 3. wird zuerst ausgeführt. dann die zweite Aufgabe, weil sie eine höhere Priorität als die erste Aufgabe hat. Ich hoffe das klärt es.

Obwohl einige mehr Beobachtung wird Ihnen helfen, es besser zu verstehen, was ist, wenn Sie die Prioritäten wie 7,8 und 9 gesetzt haben. So wie dies eine Vordergrundwarteschlange ist, wird 7 zuerst 7 dann 8 ausgeführt werden um eins und ausschließlich in dieser Reihenfolge und während 7 ausgeführt wird, warten 8 und 9, dh foreground Warteschlange wird synchron zueinander ausgeführt werden.

Aber Background und Idle Warteschlange wird sich nicht auf diese Weise verhalten, wobei die Ausführung asynchron zu anderen Aufgaben ist und Aufgaben der Priorität folgen. Und zuerst Background und die Idle Warteschlange.

Ich hoffe, diese Erklärung klärt in gewissem Maße.

+0

Wenn dies der Fall ist, muss die Reihenfolge der Farbe und des Textes auf dem Bildschirm "Three Two One" sein, was nicht der Fall ist. –

+0

@ Vignesh.N Ich glaube, Sie haben die Antwort nicht sorgfältig gelesen .... die Aufgabe befindet sich in der Vordergrundwarteschlange, sie werden in der Reihenfolge ausgeführt, in der sie in der Warteschlange stehen. und die Ausgabe wird "eins zwei drei" sein –

Verwandte Themen