2010-09-25 9 views
19

Ich weiß, wenn ich ein Steuerelement aus einem anderen Thread ändern, sollte ich vorsichtig sein, da WinForms und WPF nicht zulassen, den Status der Steuerung von anderen Threads zu ändern.Warum darf nur der UI-Thread die Benutzeroberfläche ändern?

Warum ist diese Einschränkung vorhanden?

Wenn ich Thread-sicheren Code schreiben kann, sollte ich in der Lage sein, den Steuerungszustand sicher zu ändern. Warum ist diese Einschränkung dann vorhanden?

+0

Ich glaube einer der Gründe, warum diese Einschränkung besteht, ist, dass es immer nicht mehr und nicht weniger als einen Thread gibt, der garantiert die Windows-Nachrichtenschleife abhören kann. –

+1

@Tim: Das ist nicht richtig, mehrere UI-Threads können existieren, jeder mit seiner eigenen Nachrichtenschleife. Zum Beispiel ist es erlaubt, einen zweiten Thread hochzufahren, um einen Fortschrittsbalken-Dialog anzuzeigen. –

+0

@Ben: Dieser Beitrag ist mit C# getaggt. In einer .NET-Anwendung müssen Sie die Steuerung immer wieder an den GUI-Thread übergeben, um zeichnungsbezogene Arbeiten auszuführen (oder riskantes Verhalten oder eine Ausnahme zu riskieren). Dies schließt natürlich nicht aus, dass Sie eine beliebige Anzahl von Threads erzeugen, um Hintergrundarbeit zu leisten. In Bezug auf das Abhören der tatsächlichen eingehenden Win32-Nachrichten ist der einzige Weg, den ich weiß, dies zu tun (abgesehen von einem externen Anruf) mit "protected override void WndProc (ref Message m)". Theoretisch könnte man die an diese Methode übergebenen Nachrichten übernehmen und sie für jeden Thread verwenden. –

Antwort

19

Mehrere GUI-Frameworks haben diese Einschränkung. Gemäß dem Buch Java Concurrency in Practice besteht der Grund dafür darin, eine komplexe Verriegelung zu vermeiden. Das Problem besteht darin, dass GUI-Steuerelemente möglicherweise auf beide Ereignisse von der Benutzeroberfläche, Datenbindung usw. reagieren müssen, was zu einem Sperren aus mehreren verschiedenen Quellen und somit zu einem Risiko von Deadlocks führt. Um dies zu vermeiden, schränkt .NET WinForms (und andere UIs) den Zugriff auf Komponenten auf einen einzelnen Thread ein und verhindert so das Sperren.

+0

Fügen Sie dazu die Interoperabilität mit ActiveX-Steuerelementen hinzu (Browser-Steuerung usw. - möglicherweise indirekt durch Winforms-Integration) und Sie haben auch ein COM-Vermächtnis, das Single Threaded Appartement ist. – TomTom

+1

Windows ** nicht ** den Zugriff auf den besitzenden Thread beschränken. Diese Einschränkung wird von .NET eingeführt. –

+0

@Ben: Das wusste ich nicht. Ich werde meine Antwort aktualisieren, um das zu reflektieren. Ich überprüfe, was das Buch sagt. –

3

.NET behält sich das Recht vor, jederzeit auf das Steuerelement in dem Thread zuzugreifen, in dem Sie es erstellt haben. Daher können Zugriffe, die von einem anderen Thread kommen, nie Thread-sicher sein.

8

Wenn ein Fenster erstellt wird, werden bei der Erstellung eines Steuerelements UI-Aktualisierungen über Nachrichten von einer Nachrichtenpumpe ausgeführt. Der Programmierer hat keine direkte Steuerung des Fadens, auf dem die Pumpe läuft, daher könnte das Eintreffen einer Nachricht für eine Steuerung möglicherweise zu einer Änderung des Zustands der Steuerung führen. Wenn einem anderen Thread (dem der Programmierer direkt Kontrolle hatte) erlaubt wurde, den Zustand der Steuerung zu ändern, müsste eine Art von Synchronisationslogik eingerichtet werden, um eine Beschädigung des Steuerungszustands zu verhindern. Die Steuerelemente in .Net sind nicht Thread-sicher; das ist, vermute ich durch Design. Synchronisierungslogik in allen Steuerelementen zu erstellen wäre in Bezug auf das Entwerfen, Entwickeln, Testen und Unterstützen des Codes, der diese Funktion bereitstellt, teuer. Der Programmierer könnte natürlich die Thread-Sicherheit für seinen eigenen Code zur Verfügung stellen, aber nicht für den Code, der in .Net ist, der gleichzeitig mit seinem Code läuft. Eine Lösung für dieses Problem besteht darin, diese Arten von Aktionen auf einen Thread und nur auf einen Thread zu beschränken, wodurch der Kontrollcode in .Net einfacher zu verwalten ist.

1

Sie könnten Ihren eigenen Code threadsicher machen, aber es gibt keine Möglichkeit für Sie, die notwendigen Synchronisationsgrundelemente in den eingebauten WinForm- und WPF-Code zu injizieren, die mit denen in Ihrem Code übereinstimmen. Denken Sie daran, dass hinter den Kulissen viele Nachrichten herumgereicht werden, die schließlich dazu führen, dass der UIhread auf das Steuerelement zugreift, ohne dass Sie es jemals wirklich bemerken.

Ein weiterer interessanter Aspekt einer Steuer-Thread-Affinität ist, dass es könnte (obwohl ich vermute, dass sie nie würde) verwenden Sie die Thread Local Storage Muster. Wenn Sie auf ein Steuerelement in einem anderen Thread als dem, auf dem es erstellt wurde, zugegriffen haben, ist es natürlich nicht möglich, auf die richtigen TLS-Daten zuzugreifen, ganz gleich, wie sorgfältig Sie den Code strukturiert haben, um alle normalen Multithread-Probleme zu vermeiden.

1

Eigentlich, soweit ich weiß, war das der Plan von Anfang an! Auf jedes Steuerelement kann von jedem Thread aus zugegriffen werden! Und nur weil Threads gesperrt werden mussten, wenn ein anderer Thread Zugriff auf das Steuerelement benötigte - und weil das Sperren teuer ist - wurde ein neues Threading-Modell namens "Thread-Vermietung" erstellt. In diesem Modell würden verwandte Steuerelemente mit nur einem Thread in "Kontexten" aggregiert, wodurch die erforderliche Sperrung reduziert wird. Ziemlich cool, nicht wahr?

Leider war dieser Versuch zu kühn, um erfolgreich zu sein (und ein bisschen komplexer, weil Sperren noch erforderlich war), also das gute alte Windows Forms-Threading-Modell - mit dem einzelnen UI-Thread und mit dem Erstellungs-Thread Die Kontrolle - wird wieder in WPF verwendet, um unser Leben einfacher zu machen?

1

Windows unterstützt viele Operationen, die, besonders in Kombination verwendet, inhärent nicht Thread-sicher sind. Was passiert beispielsweise, wenn ein Thread versucht, Text in ein Textfeld einzufügen, das mit dem 50. Zeichen beginnt, während ein anderer Thread versucht, die ersten 40 Zeichen aus diesem Feld zu löschen? Es wäre möglich, dass Windows Sperren verwendet, um sicherzustellen, dass die zweite Operation nicht bis zum Abschluss des ersten Vorgangs gestartet werden kann. Durch die Verwendung von Sperren würde jedoch jeder Operation zusätzlicher Aufwand entstehen. Außerdem würde die Möglichkeit eines Deadlocks auftreten, wenn Aktionen für eine Entität erforderlich sind Manipulation eines anderen. Das Erfordernis, dass Aktionen, die ein bestimmtes Fenster betreffen, in einem bestimmten Thread stattfinden müssen, ist eine strengere Anforderung, als erforderlich wäre, um die Ausführung unsicherer Kombinationen von Vorgängen zu verhindern, aber es ist relativ einfach zu analysieren. Die Verwendung von Steuerelementen aus mehreren Threads und das Vermeiden von Zusammenstößen auf andere Weise wäre im Allgemeinen schwieriger.

Verwandte Themen