Die Antwort auf diese Frage liegt in der Funktionsweise von C # Controls
Steuerelemente in Windows Forms sind an einen bestimmten Thread gebunden und nicht threadsicher. Wenn Sie die Methode eines Steuerelements von einem anderen Thread aus aufrufen, müssen Sie daher eine der Aufrufmethoden des Steuerelements verwenden, um den Aufruf für den richtigen Thread zu marshallen. Mit dieser Eigenschaft können Sie bestimmen, ob Sie eine Aufrufmethode aufrufen müssen. Dies kann hilfreich sein, wenn Sie nicht wissen, welcher Thread ein Steuerelement besitzt.
Von Control.InvokeRequired
Tatsächlich stellt Invoke sicher, dass der von Ihnen aufgerufene Code in dem Thread vorkommt, in dem das Steuerelement "lebt", und verhindert effektiv Thread-übergreifende Ausnahmen.
Aus historischer Sicht war dies in .Net 1.1 tatsächlich zulässig. Was es bedeutete, war, dass Sie versuchen konnten, Code auf dem "GUI" -Thread von jedem Hintergrund-Thread aus auszuführen, und dies würde meistens funktionieren. Manchmal wurde Ihre App einfach beendet, weil Sie den GUI-Thread effektiv unterbrochen haben, während er etwas anderes tat. Dies ist die Cross-Threaded-Ausnahme. Stellen Sie sich vor, Sie versuchen, eine TextBox zu aktualisieren, während die GUI etwas anderes malt.
- Welche Aktion hat Priorität?
- Ist es überhaupt möglich, dass beides gleichzeitig passiert?
- Was passiert mit all den anderen Befehlen, die die GUI ausführen muss?
Tatsächlich unterbrechen Sie eine Warteschlange, was viele unvorhergesehene Folgen haben kann. Invoke ist praktisch die "höfliche" Methode, um das, was Sie tun möchten, in diese Warteschlange zu bringen. Diese Regel wurde ab .NET 2.0 über eine ausgelöste InvalidOperationException erzwungen .
Um zu verstehen, was sich tatsächlich hinter den Kulissen abspielt und was unter "GUI-Thread" zu verstehen ist, ist es hilfreich zu verstehen, was eine Nachrichtenpumpe oder eine Nachrichtenschleife ist.
Dies wird bereits in der Frage " Was ist eine Nachrichtenpumpe? " Beantwortet. Es wird empfohlen, diese zu lesen, um den tatsächlichen Mechanismus zu verstehen, an den Sie bei der Interaktion mit Steuerelementen gebunden sind.
Andere Lektüre, die Sie vielleicht nützlich finden, umfasst:
Was ist los mit Begin Invoke?
Eine der Grundregeln der Windows-GUI-Programmierung ist, dass nur der Thread, der ein Steuerelement erstellt hat, auf dessen Inhalt zugreifen und / oder ihn ändern kann (mit Ausnahme einiger dokumentierter Ausnahmen). Wenn Sie dies von einem anderen Thread aus tun, erhalten Sie ein unvorhersehbares Verhalten, das von Deadlocks über Ausnahmen bis hin zu einer halb aktualisierten Benutzeroberfläche reicht. Der richtige Weg, um ein Steuerelement von einem anderen Thread zu aktualisieren, besteht darin, eine entsprechende Nachricht an die Anwendungsnachrichtenwarteschlange zu senden. Wenn die Nachrichtenpumpe diese Nachricht ausführt, wird das Steuerelement auf demselben Thread aktualisiert, der sie erstellt hat (denken Sie daran, dass die Nachrichtenpumpe auf dem Hauptthread ausgeführt wird).
und für eine codeübergreifendere Übersicht mit einem repräsentativen Beispiel:
Ungültige Cross-Thread-Operationen
// 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
}
}
Sobald Sie eine Wertschätzung für InvokeRequired haben, können Sie eine Erweiterungsmethode zum Abschluss dieser Aufrufe in Betracht ziehen. Dies wird in der Frage zum Stapelüberlauf ausführlich behandelt . Bereinigen von Code, der mit Invoke Required übersät ist .
Es gibt auch eine weitere Beschreibung dessen, was historisch passiert ist und was von Interesse sein könnte.