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 MessageBox
es 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 and
Three`?
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.
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.
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
.