Warum können Sie in WinForms keine UI-Steuerelemente aus anderen Threads aktualisieren?

StackOverflow https://stackoverflow.com/questions/10349

  •  08-06-2019
  •  | 
  •  

Frage

Ich bin mir sicher, dass es dafür einen guten (oder zumindest guten) Grund gibt.Was ist es?

War es hilfreich?

Lösung

Weil es (neben anderen Problemen) leicht zu einem Deadlock kommen kann.

Beispielsweise könnte Ihr sekundärer Thread versuchen, das UI-Steuerelement zu aktualisieren, aber das UI-Steuerelement wartet darauf, dass eine vom sekundären Thread gesperrte Ressource freigegeben wird, sodass beide Threads am Ende darauf warten, dass der andere beendet wird.Wie andere angemerkt haben, betrifft diese Situation nicht nur UI-Code, sondern kommt besonders häufig vor.

In anderen Sprachen wie C++ steht es Ihnen frei, dies zu versuchen (ohne dass eine Ausnahme wie in WinForms ausgelöst wird), aber Ihre Anwendung kann einfrieren und nicht mehr reagieren, wenn ein Deadlock auftritt.

Im Übrigen können Sie dem UI-Thread ganz einfach mitteilen, dass Sie ein Steuerelement aktualisieren möchten, indem Sie einfach einen Delegaten erstellen und dann die (asynchrone) BeginInvoke-Methode für dieses Steuerelement aufrufen und ihm Ihren Delegaten übergeben.Z.B.

myControl.BeginInvoke(myControl.UpdateFunction);

Dies entspricht dem Ausführen einer C++/MFC-PostMessage aus einem Arbeitsthread

Andere Tipps

Ich denke, das ist eine brillante Frage - und ich denke, es gibt eine bessere Antwort.

Sicherlich ist der einzige Grund, dass in einem Framework irgendwo etwas befindet, das nicht sehr fadensicher ist.

Dieses „Etwas“ ist fast jedes einzelne Instanzmitglied in jedem einzelnen Steuerelement in System.Windows.Forms.

Die MSDN-Dokumentation für viele Steuerelemente in System.Windows.Forms, wenn nicht sogar alle, sagen wir „Alle öffentlichen statischen (in Visual Basic freigegebenen) Mitglieder dieses Typs sind threadsicher.Es kann nicht garantiert werden, dass Instanzmitglieder Thread-sicher sind.

Dies bedeutet, dass Instanzmitglieder wie TextBox.Text {get; set;} sind nicht Wiedereinsteiger.

Die Thread-Sicherheit jedes dieser Instanzmitglieder könnte zu einem hohen Overhead führen, den die meisten Anwendungen nicht benötigen.Stattdessen haben die Designer des .Net-Frameworks entschieden, und ich denke richtig, dass die Last der Synchronisierung des Zugriffs auf Formularsteuerelemente aus mehreren Threads dem Programmierer auferlegt werden sollte.

[Bearbeiten]

Obwohl diese Frage nur nach dem „Warum“ fragt, finden Sie hier einen Link zu einem Artikel, der das „Wie“ erklärt:

Wie man:Führen Sie Thread-sichere Aufrufe an Windows Forms-Steuerelemente durch auf MSDN

http://msdn.microsoft.com/en-us/library/ms171728.aspx

Obwohl es vernünftig klingt, ist Johns Antwort nicht korrekt.Selbst wenn Sie Invoke verwenden, sind Sie immer noch nicht sicher, dass Sie nicht in Deadlock-Situationen geraten.Beim Umgang mit Ereignissen, die in einem Hintergrundthread mithilfe von Invoke ausgelöst wurden, kann es sogar zu diesem Problem kommen.


Der wahre Grund liegt eher in den Rennbedingungen und reicht bis in die alten Win32-Zeiten zurück.Ich kann die Details hier nicht erklären, die Schlüsselwörter sind Nachrichtenpumpen, WM_PAINT-Ereignisse und die subtilen Unterschiede zwischen „SEND“ und „POST“.


Weitere Informationen finden Sie hier Hier Und Hier.

In Version 1.0/1.1 wurde während des Debuggens keine Ausnahme ausgelöst, stattdessen kam es zu einem zeitweiligen Hängenbleiben der Laufzeit.Hübsch!:) Daher haben sie mit 2.0 dieses Szenario eine Ausnahme ausgelöst und das zu Recht.

Der eigentliche Grund dafür ist wahrscheinlich (wie Adam Haile angibt) eine Art Parallelitäts-/Sperrproblem.Beachten Sie, dass die normale .NET-API (z. B. TextBox.Text = "Hello";) SEND-Befehle umschließt (die eine sofortige Aktion erfordern), was zu Problemen führen kann, wenn sie in einem anderen Thread als dem Thread ausgeführt werden, der die Aktualisierung ausführt.Bei Verwendung von Invoke/BeginInvoke wird stattdessen ein POST verwendet, der die Aktion in die Warteschlange stellt.

Weitere Informationen zu SEND und POST Hier.

Dadurch wird verhindert, dass zwei Dinge gleichzeitig versuchen, die Steuerung zu aktualisieren.(Dies kann passieren, wenn die CPU in der Mitte eines Schreibens/Lesens zum anderen Thread umschaltet.

Bearbeiten:

In anderen Sprachen wie C ++ können Sie es frei versuchen, dies zu tun (ohne Ausnahme wird wie bei WinForms), aber Sie werden am Ende auf die harte Tour lernen!

Ahh ja ... ich wechsle zwischen C/C++ und C# und war daher etwas allgemeiner, als ich hätte sein sollen, sorry ...Er hat recht, du dürfen Tun Sie dies in C/C++, aber es wird Ihnen auf die Nerven gehen!

Es wäre auch erforderlich, die Synchronisierung innerhalb von Update-Funktionen zu implementieren, die empfindlich auf gleichzeitige Aufrufe reagieren.Dies für UI-Elemente zu tun wäre sowohl auf Anwendungs- als auch auf Betriebssystemebene kostspielig und für den Großteil des Codes völlig überflüssig.

Einige APIs bieten eine Möglichkeit, den aktuellen Thread-Besitz eines Systems zu ändern, sodass Sie Systeme vorübergehend (oder dauerhaft) von anderen Threads aktualisieren können, ohne auf die Kommunikation zwischen Threads zurückgreifen zu müssen.

Hmm, ich bin mir nicht ganz sicher, aber ich denke, wenn wir Fortschrittskontrollen wie Wartebalken oder Fortschrittsbalken haben, können wir deren Werte aus einem anderen Thread aktualisieren und alles funktioniert großartig und ohne Störungen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top