2014-09-22 9 views
15

Byłem pod wrażeniem, że dispatcher pójdą priorytet operacji kolejce ją i wykonać operacje na podstawie priorytetu lub kolejności, w którym operacja została dodana do kolejki (jeśli ten sam priorytet) , dopóki nie powiedziano mi, że tak nie jest w przypadku WPF UI dispatcher.Zrozumienie WPF Dispatcher.BeginInvoke

Powiedziano mi, że jeśli operacja na wątku UI trwa dłużej, powiedzmy, że baza danych odczytuje , program rozsyłający UI próbuje wykonać następny zestaw operacji w kolejce. Nie mogłem pogodzić się z nim, więc zdecydowałem się napisać przykładową aplikację WPF, która zawiera przycisk i trzy prostokąty, po kliknięciu przycisku prostokąty są wypełnione różnymi kolorami.

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

aw opóźnieniem kodu

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

komentowanym kod działa zgodnie z oczekiwaniami metody są wywoływane na podstawie DispatcherPriority i ja również dostać się do zobaczyć odświeżania ekranu po każdym operacja została zakończona. Order jest One, Two, Three. Kolory są rysowane jedna po drugiej.

Teraz kod działa gdzie DispatcherPriority nie wspomniano (zakładam, że to domyślnie Normal) kolejność jest nadal One, Two, Three ale jeśli wykazują MessageBox wewnątrz sposobach
Thrid popup jest pokazać pierwszy wtedy Two następnie One ale gdy debuguję, widzę, że metody są wywoływane w oczekiwanej kolejności (IntelliTrace pokazuje nawet, że okno komunikatu jest wyświetlane, ale nie widzę go na ekranie w tym czasie i widzę je dopiero po zakończeniu ostatniej operacji.) to tylko, że MessageBox es są pokazane w odwrotnej kolejności.

Czy to dlatego, że MessageBox.Show jest połączeniem blokującym, a operacja jest czyszczona po zamknięciu wiadomości.
Nawet wtedy zamówienie MessageBox powinno być również One, Two and Trzy "?

+0

Jest to po prostu zależne od kolejności zamykania skrzynek. Ponieważ ostatnia jest na wierzchu, jest to pierwsza, którą zamykasz. Nie ma nic wspólnego z DispatcherPriority. –

+0

Tak .. Tak myślałem, ale nie jestem w stanie zobaczyć innych "MessageBox" zanim zamknę najwyższy. –

+0

@ Vignesh.N czy moja odpowiedź wyjaśnia twoje zapytanie? –

Odpowiedz

1

Dzieje się tak, ponieważ pierwszy MessageBox blokuje wątek UI.

To, co robi Dispatcher.BeginInvoke() pod maską, zabiera delegata i planuje jego uruchomienie na głównym wątku UI podczas następnego okresu bezczynności. Jednak MessageBox zablokuje dowolny wątek, do którego jest wywoływany, dopóki nie zostanie zamknięty. Oznacza to, że drugi MessageBox nie może zostać wyświetlony, dopóki pierwszy nie zostanie wyczyszczony, ponieważ program planujący wątek interfejsu użytkownika widzi, że wątek jest już w użyciu (czekając na wyczyszczenie pierwszego MessageBox) i nie może wykonać następnego uczestnika zawierającego drugi MessageBox.

+0

Jeśli miałeś rację, oczekiwałbym, że okna wiadomości będą miały taką kolejność, w jakiej zostały wywołane. Stąd 1, 2, 3 nie 3, 2 1 ... – blueprint

+0

Rzeczywiście, jednak problem z zamówieniem tutaj najprawdopodobniej można prześledzić do zagnieżdżonych BeginInvokes. Zewnętrzny BeginInvoke zapewnia już, że kod działa w interfejsie użytkownika. Samo usunięcie wewnętrznych BeginInvokes wystarczyłoby do zapewnienia, że ​​kod zostanie wykonany w kolejności. Wywołanie BeginInvoke z wątku UI może łatwo mieć nieoczekiwane zachowania, ponieważ jesteś już w wątku UI. –

+0

Masz całkowitą rację, sam tego spróbowałem. Próbowałem też kilka razy, aby naprawdę zrozumieć, co dzieje się za kulisami w tym przypadku, ale jednak próbuję na to spojrzeć, doszedłem do tego samego wniosku: powinno być 1, 2, 3. Czy możesz wyjaśnić bardziej szczegółowo jaki jest tutaj twój model wewnętrznych działań? Co dokładnie dzieje się? – blueprint

4

Przed przejściem do zachowania kodu jest warunkiem wstępnym zrozumienia priorytetów Dispatcher. DispatcherPriority jest podzielony na zakresy, jak pokazano na poniższym obrazku.

DispatcherPriority

Jeśli po prostu kolejka 4 Działania do 4 powyżej zakresy na Dispatcher. kolejka Foreground zostanie wykonana jako pierwsza, a następnie , a następnie w ostatniej kolejce.priorytet 0 nie zostanie wykonany.

Teraz Twój kod:

Trzy zadania są ustawiane w kolejce 1. miejsce w background, 2nd w background i 3. w foreground kolejce. Więc trzeci zostanie wykonany jako pierwszy. to drugie zadanie powoduje, że ma wyższy priorytet niż pierwsze zadanie. Mam nadzieję, że to rozwiąże.

Chociaż trochę więcej obserwacji pomoże ci to lepiej zrozumieć, co jeśli ustawisz priorytety na 7,8 i 9. Tak więc, ponieważ jest to kolejka pierwszego planu, 7 zostanie wykonane najpierw jako 7, a potem 8. Jeden przez jedną i wyłącznie w tej kolejności, a gdy 7 zostanie wykonane, 8 i 9 będą czekać, co oznacza, że ​​kolejka zostanie wykonana synchronicznie względem siebie.

Kolejki nie zachowują się w taki sposób, w którym wykonanie jest asynchroniczne z innymi zadaniami, a zadania będą wykonywane zgodnie z priorytetem. I pierwsza kolejka Background i kolejka Idle.

Mam nadzieję, że to wyjaśnienie w pewnym stopniu wyjaśnia.

+0

jeśli jest to prawdą, kolejność kolorów i tekstu na ekranie ma wartość "Three Two One", co nie ma miejsca. –

+0

@ Vignesh.N Myślę, że nie przeczytałeś odpowiedzi dokładnie ... zadanie jest w kolejce pierwszego planu, zostaną wykonane w serii, w której są w kolejce. a wyjście będzie "jeden dwa trzy" –