web-dev-qa-db-fra.com

Compréhension de WPF Dispatcher.BeginInvoke

J'avais l'impression que la dispatcher suivrait la priorité Des opérations qui l'ont mise en file d'attente et exécutait les opérations en fonction de la priorité Ou de l'ordre dans lequel l'opération a été ajoutée à la file d'attente (avec la même priorité) jusqu'à ce qu'on m'ait dit que ce n'était pas le cas dans le cas du WPF UI dispatcher

On m'a dit que si une opération sur le thread d'interface utilisateur prend plus de temps, disons une base de données lue Le répartiteur d'interface utilisateur essaie d'exécuter le prochain ensemble d'opérations dans la file d'attente . Je ne pouvais pas en venir à bout alors Écrivez un exemple d’application WPF contenant un bouton et trois rectangles. Sur un clic de bouton, les rectangles sont remplis de couleurs différentes.

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

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

Le code commenté fonctionne comme prévu. Les méthodes sont appelées sur la base de la variable DispatcherPriority. Je peux également voir l'actualisation de l'écran après chaque opération terminée .Order est One, Two, Three. Les couleurs sont dessinées les unes après les autres.

Maintenant, le code de travail où la DispatcherPriority n’est pas mentionnée (Je suppose que ce serait par défaut à Normal) la commande est toujours One, Two, Three mais si j’affiche une MessageBox dans les méthodes,
Thrid popup est affiché en premier puis Two puis One mais quand je débogue, je pouvais voir les méthodes
invoqué dans l'ordre attendu (IntelliTrace indique même qu'une boîte de message est affichée, mais je ne la vois pas à l'écran à ce moment-là et je ne la vois qu'après la fin de la dernière opération.) C'est juste que les MessageBoxes sont affichées à l'inverse. ordre.

Est-ce parce que MessageBox.Show est un appel bloquant et que l'opération est effacée après la fermeture du message.
Même dans ce cas, l'ordre de MessageBox devrait également être One, Two andThree`?

15
Vignesh.N

Avant de revenir au comportement de votre code, il est indispensable de comprendre les priorités de Dispatcher. DispatcherPriority est divisé en plages, comme indiqué dans l'image ci-dessous.

 DispatcherPriority

Si vous mettez simplement en file d'attente 4 actions sur les 4 ci-dessus, cliquez sur Dispatcher. la file d'attente Foreground sera d'abord exécutée, puis la file d'attente Background et ensuite dans la dernière file d'attente Idle. la priorité 0 ne sera pas exécutée.

Maintenant votre code:

Trois tâches sont mises en file d'attente: 1ère dans background, 2ème dans background et 3ème dans la file d'attente foreground. Donc 3ème sera exécuté en premier. alors 2ème tâche car sa priorité est supérieure à celle de la 1ère tâche. J'espère que ça efface.

Bien que quelques observations supplémentaires vous aident à mieux comprendre, par exemple si vous avez défini les priorités 7,8 et 9. Donc, comme il s’agit d’une file de premier plan, 7 sera exécuté en premier, puis 7 et ensuite 8. Un par un et exclusivement. dans cet ordre et pendant l'exécution de 7, 8 et 9 attendent, ce qui signifie que la file d'attente foreground sera exécutée de manière synchrone.

Mais les files d'attente Background et Idle ne se comporteront pas de la sorte, où l'exécution est asynchrone par rapport à d'autres tâches et les tâches suivront la priorité. Et d'abord Background et la file d'attente Idle.

J'espère que cette explication clarifie dans une certaine mesure.

3
Kylo Ren

Cela est dû au fait que la première MessageBox bloque le thread UI .

Ce que Dispatcher.BeginInvoke() fait sous le capot emmène votre délégué et le planifie pour qu'il soit exécuté sur le thread principal de l'interface utilisateur lors de sa prochaine période d'inactivité. Cependant, MessageBox bloquera le thread à partir duquel il est appelé jusqu'à ce qu'il soit fermé. Cela signifie que la seconde MessageBox ne peut pas être affichée tant que la première n'est pas effacée car le planificateur de thread d'interface utilisateur voit que le thread est déjà utilisé (en attente de la première MessageBox à effacer) et ne peut pas exécuter le prochain délégué contenant la seconde MessageBox.

1
Adam Wilson