Odpowiedź na to pytanie dotyczy sposobu działania kontrolek języka C #
Kontrolki w Windows Forms są powiązane z określonym wątkiem i nie są bezpieczne dla wątków. W związku z tym, jeśli wywołujesz metodę kontrolki z innego wątku, musisz użyć jednej z metod wywołania kontrolki, aby zorganizować wywołanie do odpowiedniego wątku. Ta właściwość może służyć do określenia, czy należy wywołać metodę invoke, co może być przydatne, jeśli nie wiesz, który wątek jest właścicielem formantu.
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.
- Które działanie ma pierwszeństwo?
- Czy w ogóle możliwe jest, aby oba miały miejsce jednocześnie?
- Co dzieje się z pozostałymi poleceniami wymaganymi przez GUI?
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
Jedną z głównych zasad programowania GUI systemu Windows jest to, że tylko wątek, który utworzył formant, może uzyskać dostęp i / lub modyfikować jego zawartość (z wyjątkiem kilku udokumentowanych wyjątków). Spróbuj zrobić to z dowolnego innego wątku, a uzyskasz nieprzewidywalne zachowanie, od zakleszczenia, przez wyjątki po w połowie zaktualizowany interfejs użytkownika. Właściwym sposobem zaktualizowania formantu z innego wątku jest wysłanie odpowiedniego komunikatu do kolejki komunikatów aplikacji. Kiedy pompa komunikatów podejmie się wykonania tego komunikatu, formant zostanie zaktualizowany w tym samym wątku, który go utworzył (pamiętaj, że pompa komunikatów działa w wątku głównym).
i, aby uzyskać bardziej rozbudowany przegląd kodu z reprezentatywną próbką:
Nieprawidłowe operacje między wątkami
public delegate void ControlStringConsumer(Control control, string text);
public void SetText(Control control, string text) {
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text});
} else {
control.Text=text;
}
}
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.