Jaka jest różnica między Invoke () a BeginInvoke ()


398

Po prostu zastanawiasz się, jaka jest różnica między BeginInvoke()i Invoke()są?

Głównie do czego każdy z nich zostanie wykorzystany.

EDYCJA: Jaka jest różnica między utworzeniem obiektu wątku a wywołaniem invoke w tym przypadku i wywołaniem BeginInvoke()delegata? czy są to to samo?

Odpowiedzi:


568

Masz na myśli Delegate.Invoke/ BeginInvokelub Control.Invoke/ BeginInvoke?

  • Delegate.Invoke: Wykonuje się synchronicznie w tym samym wątku.
  • Delegate.BeginInvoke: Wykonuje się asynchronicznie w threadpoolwątku.
  • Control.Invoke: Wykonuje się w wątku interfejsu użytkownika, ale wątek wywołujący czeka na zakończenie przed kontynuowaniem.
  • Control.BeginInvoke: Wykonuje się w wątku interfejsu użytkownika, a wywołanie wątku nie czeka na zakończenie.

Odpowiedź Tima wspomina, kiedy możesz chcieć użyć BeginInvoke- Delegate.BeginInvokepodejrzewam, że była głównie nastawiona na .

W przypadku aplikacji Windows Forms sugeruję, że zwykle powinieneś używać BeginInvoke. W ten sposób nie musisz martwić się na przykład o zakleszczenie - ale musisz zrozumieć, że interfejs użytkownika mógł nie zostać zaktualizowany do następnego spojrzenia! W szczególności nie należy modyfikować danych, które wątek interfejsu użytkownika może być używany do celów wyświetlania. Na przykład, jeśli masz Personz FirstNamei LastNamewłaściwości, a nie:

person.FirstName = "Kevin"; // person is a shared reference
person.LastName = "Spacey";
control.BeginInvoke(UpdateName);
person.FirstName = "Keyser";
person.LastName = "Soze";

Wtedy interfejs użytkownika może wyświetlać „Keyser Spacey”. (Istnieje szansa z zewnątrz, że może wyświetlić „Kevin Soze”, ale tylko z powodu dziwności modelu pamięci.)

Jeśli nie masz tego rodzaju problemu, Control.BeginInvokełatwiej jest rozwiązać problem i uniknąć konieczności oczekiwania przez wątek w tle bez uzasadnionego powodu. Zauważ, że zespół Windows Forms zagwarantował, że możesz używać Control.BeginInvokew sposób „odpal i zapomnij” - tj. Bez konieczności dzwonienia EndInvoke. Nie dotyczy to ogólnie wywołań asynchronicznych: zwykle każde BeginXXX powinno mieć odpowiadające wywołanie EndXXX, zwykle w wywołaniu zwrotnym.


4
Dlaczego więc ppl miałby używać Invoke zamiast BeingInvoke? Nie powinno być żadnych korzyści w porównaniu z użyciem Invoke. Oba wykonują procesy w tle, tylko czy jeden jest w tym samym wątku, a drugi w innym wątku?
jeeen

2
@Jon: Podczas korzystania z narzędzia Dispatcher.BeginInvoke mój kod działa poprawnie i w programie Dispatcher.Odwołaj moją aplikację, zmuszając mnie do czekania przez kilka sekund, a następnie inicjuje wszystkie elementy sterujące, a następnie uruchamia się. Czy możesz mi pomóc dowiedzieć się, w którym dokładnie miejscu utknąłem? ?
SharpUrBrain

6
@SharpUrBrain: Control.BeginInvoke jest swego rodzaju odpowiednikiem Dispatcher.BeginInvoke, ale dla WinForm (podczas gdy Dispatcher jest dla WPF i Silverlight).
Jon Skeet

4
@SharpUrBrain: Sugeruję, abyś zadał konkretne pytanie zamiast kontynuować komentarze - i oczywiście sprawdź, czy to samo pytanie zostało już wcześniej zadane przez kogoś innego.
Jon Skeet

2
@AZ: Tak, przez „w wątku interfejsu użytkownika” ma na myśli konkretny „wątek interfejsu użytkownika”, do którego należy uchwyt danego elementu sterującego. Zazwyczaj jest tylko jeden wątek interfejsu użytkownika, ale możliwe jest posiadanie wielu wątków interfejsu użytkownika, aw zaawansowanych aplikacjach istnieją powody, dla których chcesz je mieć. Technicznie, każdy (normalny?) Wątek może uruchomić pompę komunikatów interfejsu użytkownika i stać się wątkiem interfejsu użytkownika - a później może wyłączyć pompę komunikatów i przestać być wątkiem interfejsu użytkownika. (Zakładam, że nie jest to coś, co można wypróbować na wątku puli wątków.)
Rob Parker,

46

Opierając się na odpowiedzi Jona Skeeta, zdarza się, że chcesz wywołać delegata i poczekać na zakończenie jego wykonywania, zanim bieżący wątek będzie kontynuowany. W takich przypadkach połączenie Invoke jest tym, czego chcesz.

W aplikacjach wielowątkowych możesz nie chcieć, aby wątek czekał na delegata na zakończenie wykonywania, szczególnie jeśli ten delegat wykonuje operacje we / wy (co może spowodować, że delegat i blok wątków).

W takich przypadkach przydatny byłby BeginInvoke. Dzwoniąc do niego, mówisz delegatowi, aby zaczął, ale wtedy twój wątek może robić inne rzeczy równolegle z delegatem.

Korzystanie z BeginInvoke zwiększa złożoność kodu, ale są chwile, kiedy lepsza wydajność jest warta złożoności.


27

Różnica między Control.Invoke()i Control.BeginInvoke()jest

  • BeginInvoke()zaplanuje akcję asynchroniczną w wątku GUI. Po zaplanowaniu akcji asynchronicznej kod jest kontynuowany. Jakiś czas później (nie wiesz dokładnie, kiedy) twoja asynchroniczna akcja zostanie wykonana
  • Invoke() wykona akcję asynchroniczną (w wątku GUI) i poczeka, aż akcja się zakończy.

Logicznym wnioskiem jest to, że przekazany delegat Invoke()może mieć parametry wyjściowe lub wartość zwracaną, podczas gdy przekazany delegat nie BeginInvoke()może (musisz użyć EndInvoke, aby pobrać wyniki).


20

Wystarczy podać krótki, działający przykład, aby zobaczyć efekt ich różnicy

new Thread(foo).Start();

private void foo()
{
  this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
    (ThreadStart)delegate()
    {
        myTextBox.Text = "bing";
        Thread.Sleep(TimeSpan.FromSeconds(3));
    });
  MessageBox.Show("done");
}

Jeśli używasz BeginInvoke , MessageBox wyskakuje jednocześnie z aktualizacją tekstu. Jeśli używasz Invoke , MessageBox wyskakuje po 3 sekundowym śnie. Stąd pokazanie efektu połączenia asynchronicznego ( BeginInvoke ) i synchronicznego ( Invoke ).


9

Delegate.BeginInvoke () asynchronicznie kolejkuje wywołanie delegata i natychmiast zwraca kontrolę. Korzystając z Delegate.BeginInvoke (), należy wywołać Delegate.EndInvoke () w metodzie zwrotnej, aby uzyskać wyniki.

Delegate.Invoke () synchronicznie wywołuje delegata w tym samym wątku.

Artykuł MSDN


8

Wystarczy dodać, dlaczego i kiedy używać Invoke ().

Zarówno Invoke (), jak i BeginInvoke () zmieniają kod podany w wątku dyspozytora.

Ale w przeciwieństwie do BeginInvoke (), Invoke () wstrzymuje wątek, dopóki dyspozytor nie wykona kodu. Możesz użyć Invoke (), jeśli chcesz zatrzymać operację asynchroniczną, dopóki użytkownik nie dostarczy jakiegoś sprzężenia zwrotnego.

Na przykład możesz wywołać Invoke (), aby uruchomić fragment kodu, który pokazuje okno dialogowe OK / Anuluj. Po kliknięciu przycisku przez użytkownika i zakończeniu działania ustawionego kodu, zostanie zwrócona metoda invoke (), a użytkownik będzie mógł zareagować na odpowiedź użytkownika.

Zobacz Pro WPF w C # rozdział 31

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.