2014-09-22 5 views
15

Ho avuto l'impressione che il dispatcher seguirà la priorità delle operazioni in coda e di eseguire le operazioni in base alla priorità o l'ordine in cui l'operazione è stata aggiunta alla coda (se stessa priorità) fino a quando non mi è stato detto che questo non è il caso nel caso dello WPF UI dispatcher.Capire il WPF Dispatcher.BeginInvoke

Mi è stato detto che se un'operazione sul thread dell'interfaccia utente richiede una maggiore durata, dire che un database legge il dispatcher dell'interfaccia utente tenta semplicemente di eseguire il successivo gruppo di operazioni nella coda. Non ho potuto venire a patti con esso, quindi ho deciso di scrivere un'applicazione WPF di esempio che contiene un pulsante e tre rettangoli, al clic del pulsante, i rettangoli sono riempiti con colori diversi.

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

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

Il codice commentato funziona come previsto i metodi vengono richiamati in base alla DispatcherPriority e ho anche arrivare a vedere l'aggiornamento dello schermo dopo ogni operazione è stata completata. Order è One, Two, Three. I colori sono disegnati uno dopo l'altro.

Ora il codice di lavoro in cui l'DispatcherPriority non è menzionato (presumo che sarebbe di default per Normal) l'ordine è ancora One, Two, Three ma se io mostro un MessageBox all'interno dei metodi, il
Thrid comparsa è mostrare in primo luogo poi Two poi One ma quando eseguo il debug, vedo che i metodi sono
richiamati nell'ordine previsto (IntelliTrace mostra anche che viene visualizzata una finestra di messaggio ma non la vedo sullo schermo in quel momento e la vedo solo dopo che è stata completata l'ultima operazione.) è solo che gli MessageBox sono mostrati nell'ordine inverso.

È perché MessageBox.Show è una chiamata di blocco e l'operazione viene cancellata dopo che il messaggio è stato chiuso.
Anche in questo caso l'ordine di MessageBox dovrebbe essere One, Two and Tre`?

+0

Questo è semplicemente influenzato dall'ordine in cui si chiudono le finestre di messaggio. Poiché l'ultimo è in cima, questo è il primo che chiudi. Non ha nulla a che fare con DispatcherPriority. –

+0

Sì .. Ho pensato così, ma non sono in grado di vedere l'altro 'MessageBox'es prima di chiudere quello più in alto. –

+0

@ Vignesh.N la mia risposta spiega la tua domanda? –

risposta

1

Questo perché il primo MessageBox sta bloccando il thread dell'interfaccia utente.

Ciò che sta facendo è prendere il delegato e pianificarlo per essere eseguito sul thread principale dell'interfaccia utente durante il suo prossimo periodo di inattività. Tuttavia, MessageBox bloccherà qualsiasi thread da cui viene chiamato fino a quando non viene chiuso. Ciò significa che il secondo MessageBox non può essere visualizzato fino a quando il primo non viene cancellato perché lo scheduler dell'interfaccia utente vede che il thread è già in uso (in attesa del primo MessageBox da cancellare) e non può eseguire il successivo delegato che contiene il secondo MessageBox.

+0

Se avessi ragione, mi aspetterei che le finestre di messaggio siano nell'ordine in cui sono state chiamate. Quindi 1, 2, 3 not 3, 2 1 ... – blueprint

+0

In effetti, tuttavia, il problema relativo all'ordine di esecuzione qui può essere probabilmente tracciato a BeginInvokes nidificati. Il BeginInvoke esterno garantisce già che il codice sia in esecuzione sull'interfaccia utente. Rimuovere semplicemente il BeginInvokes interno sarebbe sufficiente per garantire che il codice sia eseguito in ordine. Chiamare BeginInvoke dal thread dell'interfaccia utente potrebbe facilmente avere comportamenti imprevisti poiché si è già sul thread dell'interfaccia utente. –

+0

Hai perfettamente ragione, l'ho provato io stesso. Ho anche provato un paio di volte a capire veramente cosa sta succedendo dietro le quinte in questo caso, ma comunque provo a guardarlo e sono giunto alla stessa conclusione: dovrebbe essere 1, 2, 3. Puoi spiegare più dettagliatamente qual è il tuo modello del funzionamento interno qui? Cosa succede esattamente? – blueprint

4

Prima di seguire il comportamento del codice, è un prerequisito per comprendere le priorità di Dispatcher. DispatcherPriority è diviso in intervalli come mostrato nell'immagine sottostante.

DispatcherPriority

Se semplicemente coda 4 azioni a 4 di cui sopra gamme su Dispatcher. la coda Foreground verrà eseguita per prima, quindi la Background e quindi nell'ultima coda Idle.la priorità 0 non verrà eseguita.

Ora il tuo codice:

Tre compito sono in coda prima di background, 2 ° nel background e 3 ° in foreground coda. Quindi 3rd verrà eseguito per primo. quindi 2 ° compito perché ha priorità più alta del 1 ° compito. Spero che lo chiarisca.

Anche se qualche osservazione in più ti aiuterà a capire meglio come, cosa succede se hai impostato le priorità come 7,8 e 9. Siccome questa è una coda in primo piano, 7 verrà eseguita prima poi 7 e poi 8. Uno da uno ed esclusivamente in questo ordine e mentre 7 viene eseguito, 8 e 9 aspetteranno, ovvero la coda foreground verrà eseguita in sincronia tra loro.

Ma la coda Background e Idle non si comporterà in questo modo dove l'esecuzione è asincrona ad altre attività e le attività seguiranno la priorità. E il primo Background e la coda Idle.

Spero che questa spiegazione chiarisca in una certa misura.

+0

se questo è vero, l'ordine del colore e del testo sullo schermo deve essere "Tre due" che non è il caso. –

+0

@ Vignesh.N Penso che tu non abbia letto attentamente la risposta .... l'attività è in coda in primo piano, verranno eseguite nella serie in coda. e l'output sarà "uno due tre" –