Czy ktoś może wyjaśnić to oświadczenie napisane pod tym linkiem
Invoke(Delegate):
Wykonuje określony delegat w wątku, który jest właścicielem uchwytu okna bazowego kontrolki.
Czy ktoś może wyjaśnić, co to znaczy (zwłaszcza ten odważny). Nie jestem w stanie tego jasno zrozumieć
Odpowiedzi:
Odpowiedź na to pytanie dotyczy sposobu działania kontrolek języka C #
Z Control.InvokeRequired
W efekcie funkcja Invoke zapewnia, że wywoływany kod występuje w wątku, w którym formant „żyje”, skutecznie zapobiegając wyjątkom krzyżowym.
Z historycznego punktu widzenia, w .Net 1.1 było to faktycznie dozwolone. Oznaczało to, że możesz spróbować wykonać kod w wątku „GUI” z dowolnego wątku w tle, a to w większości zadziała. Czasami powodowało to po prostu zamknięcie aplikacji, ponieważ skutecznie przerywałeś wątek GUI, gdy wykonywał coś innego. To jest wyjątek wielowątkowy - wyobraź sobie, że próbujesz zaktualizować TextBox, podczas gdy GUI maluje coś innego.
Skutecznie przerywasz kolejkę, co może mieć wiele nieprzewidzianych konsekwencji. Invoke jest w rzeczywistości „grzecznym” sposobem umieszczania w kolejce tego, co chcesz zrobić, a ta reguła była wymuszana od .Net 2.0 i dalej przez rzucany InvalidOperationException .
Aby zrozumieć, co faktycznie dzieje się za kulisami i co oznacza „wątek GUI”, warto zrozumieć, czym jest pompa komunikatów lub pętla komunikatów.
W rzeczywistości odpowiedź na to pytanie znajduje się już w pytaniu „ Co to jest pompa komunikatów ” i jest zalecana do przeczytania w celu zrozumienia faktycznego mechanizmu, z którym się łączysz podczas interakcji z kontrolami.
Inne przydatne pozycje to:
Co się dzieje z Begin Invoke
i, aby uzyskać bardziej rozbudowany przegląd kodu z reprezentatywną próbką:
Nieprawidłowe operacje między wątkami
// the canonical form (C# consumer) public delegate void ControlStringConsumer(Control control, string text); // defines a delegate type public void SetText(Control control, string text) { if (control.InvokeRequired) { control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself } else { control.Text=text; // the "functional part", executing only on the main thread } }
Gdy już docenisz InvokeRequired, możesz rozważyć użycie metody rozszerzającej do zawijania tych wywołań. Jest to dobrze omówione w pytaniu o przepełnienie stosu Czyszczenie kodu zaśmieconego z wymaganym wywołaniem .
Istnieje również dalszy opis wydarzeń historycznych, które mogą być interesujące.
źródło
Formant lub obiekt okna w Windows Forms to po prostu otoka wokół okna Win32 identyfikowanego przez uchwyt (czasami nazywany HWND). Większość czynności wykonywanych z formantem ostatecznie spowoduje wywołanie interfejsu API Win32, które używa tego uchwytu. Uchwyt jest własnością wątku, który go utworzył (zazwyczaj wątek główny) i nie powinien być manipulowany przez inny wątek. Jeśli z jakiegoś powodu musisz coś zrobić z kontrolką z innego wątku, możesz użyć
Invoke
poprosić główny wątek, aby zrobił to w Twoim imieniu.Na przykład, jeśli chcesz zmienić tekst etykiety z wątku roboczego, możesz zrobić coś takiego:
theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
źródło
this.Invoke(() => this.Enabled = true);
Cokolwiek sięthis
odnosi, jest z pewnością w bieżącym wątku, prawda?Jeśli chcesz zmodyfikować formant, należy to zrobić w wątku, w którym formant został utworzony. Ta
Invoke
metoda umożliwia wykonywanie metod w skojarzonym wątku (wątku, który jest właścicielem uchwytu okna bazowego kontrolki).W poniższym przykładzie thread1 zgłasza wyjątek, ponieważ SetText1 próbuje zmodyfikować textBox1.Text z innego wątku. Ale w wątku2 akcja w SetText2 jest wykonywana w wątku, w którym został utworzony TextBox
private void btn_Click(object sender, EvenetArgs e) { var thread1 = new Thread(SetText1); var thread2 = new Thread(SetText2); thread1.Start(); thread2.Start(); } private void SetText1() { textBox1.Text = "Test"; } private void SetText2() { textBox1.Invoke(new Action(() => textBox1.Text = "Test")); }
źródło
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
źródło
W praktyce oznacza to, że delegat ma gwarancję wywołania w głównym wątku. Jest to ważne, ponieważ w przypadku kontrolek systemu Windows, jeśli nie zaktualizujesz ich właściwości w głównym wątku, albo nie zobaczysz zmiany, albo kontrolka zgłosi wyjątek.
Wzór to:
void OnEvent(object sender, EventArgs e) { if (this.InvokeRequired) { this.Invoke(() => this.OnEvent(sender, e); return; } // do stuff (now you know you are on the main thread) }
źródło
this.Invoke(delegate)
upewnij się, że wywołujesz delegata argument dothis.Invoke()
głównego wątku / utworzonego wątku.Mogę powiedzieć, że reguła Thumb nie ma dostępu do twoich formantów formularza z wyjątkiem głównego wątku.
Poniższe wiersze mogą mieć sens przy używaniu Invoke ()
private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. if (this.textBox1.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } }
Istnieją sytuacje, gdy tworzysz wątek Threadpool (tj. Wątek roboczy), który będzie działał w wątku głównym. Nie utworzy nowego wątku, ponieważ główny wątek jest dostępny do przetwarzania dalszych instrukcji. Więc najpierw sprawdź, czy bieżący wątek jest głównym wątkiem, używając
this.InvokeRequired
if zwraca true, że bieżący kod działa w wątku roboczym, więc wywołaj this.Invoke (d, new object [] {text});w przeciwnym razie bezpośrednio zaktualizuj formant interfejsu użytkownika (tutaj masz gwarancję, że uruchamiasz kod w głównym wątku).
źródło
Oznacza to, że delegat będzie działał w wątku interfejsu użytkownika, nawet jeśli wywołasz tę metodę z procesu roboczego w tle lub wątku puli wątków. Elementy interfejsu użytkownika mają powinowactwo do wątków - lubią rozmawiać bezpośrednio tylko z jednym wątkiem: wątkiem interfejsu użytkownika. Wątek interfejsu użytkownika jest zdefiniowany jako wątek, który utworzył wystąpienie formantu i dlatego jest skojarzony z uchwytem okna. Ale to wszystko jest szczegółem implementacji.
Kluczowa kwestia jest taka: wywołałbyś tę metodę z wątku roboczego, aby uzyskać dostęp do interfejsu użytkownika (aby zmienić wartość w etykiecie itp.) - ponieważ nie możesz tego zrobić z żadnego innego wątku niż wątek interfejsu użytkownika.
źródło
Delegaty są zasadniczo wbudowane
Action
lubFunc<T>
. Możesz zadeklarować delegata poza zakresem metody, którą uruchamiasz lub używająclambda
expression (=>
); ponieważ uruchamiasz delegata w ramach metody, uruchamiasz go w wątku, który jest uruchamiany dla bieżącego okna / aplikacji, który jest pogrubiony.Przykład Lambda
int AddFiveToNumber(int number) { var d = (int i => i + 5); d.Invoke(number); }
źródło
Oznacza to, że przekazany delegat jest wykonywany w wątku, który utworzył obiekt Control (który jest wątkiem interfejsu użytkownika).
Musisz wywołać tę metodę, gdy aplikacja jest wielowątkowa i chcesz wykonać jakąś operację interfejsu użytkownika z wątku innego niż wątek interfejsu użytkownika, ponieważ jeśli po prostu spróbujesz wywołać metodę na kontrolce z innego wątku, otrzymasz System.InvalidOperationException.
źródło