Wir haben ein riesiges winforms-Projekt aufgebaut, das bereits seit mehreren Jahren läuft.
Manchmal erhalten unsere Benutzer eine Ausnahme, die aussieht wie dieses .
Die Lösung dieses Problems scheint zu sein:
greift nicht auf UI-Komponenten von einem Hintergrundthread zu
.
Aber da unser Projekt ein sehr großes Projekt mit vielen verschiedenen Themen ist, gelingt es uns nicht, all das zu finden.
Gibt es eine Möglichkeit zu überprüfen (mit einigen Werkzeug oder Debugging-Option), welche Komponenten von einem Hintergrund-Thread aufgerufen werden?
Zur Klarstellung:
Ich habe ein winforms-Beispielprojekt mit einem einzelnen Form
erstellt, das zwei Button
Die Hintergrundfarbe von button2
wird beim Klicken auf die Schaltfläche auf rot gesetzt. Dies geschieht in einem Hintergrundthread (was als schlechtes Verhalten angesehen wird). Es wirft jedoch (sofort) keine Ausnahme aus. Ich möchte einen Weg finden, dies als "schlechtes Benehmen" zu erkennen. Vorzugsweise durch Scannen meines Codes, aber wenn es nur durch Debugging möglich ist (also pausieren, sobald auf eine UI-Komponente von einem Hintergrundthread zugegriffen wird), ist es auch in Ordnung.
Ich habe zwei Empfehlungen für die Verwendung zusammen, das erste ist ein Visual Studio-Plug-in namens DebugSingleThread .
Sie können alle Threads einfrieren und einzeln daran arbeiten (natürlich die Nicht-Haupt-UI-Threads) und sehen, dass jeder Thread Zugriff auf Steuerelemente hat. Langweilig Ich weiß aber nicht so schlecht mit der zweiten Methode .
Die zweite Methode besteht darin, die Schritte zu erhalten, um das Problem zu reproduzieren. Wenn Sie die Schritte kennen, um es zu reproduzieren, wird es einfacher zu sehen, was es verursacht. Um dies zu tun, habe ich dieses Benutzeraktionsprotokoll auf Github erstellt.
Es zeichnet jede Aktion auf, die ein Benutzer ausführt. Lesen Sie dazu hier auf SO: Benutzeraktivitätsprotokollierung, Telemetrie (und Variablen in globalen Ausnahmebehandlungsroutinen) .
Ich würde empfehlen, dass Sie auch die Thread-ID protokollieren. Wenn Sie das Problem reproduzieren konnten, gehen Sie zum Ende des Protokolls und erarbeiten Sie die genauen Schritte. Es ist nicht so schmerzhaft, wie es scheint und es ist großartig für die Anwendung Telemetrie.
Sie können dieses Projekt möglicherweise anpassen, z. B. ein DataSource_Completed-Ereignis abfangen oder eine Dummy-DataSource-Eigenschaft hinzufügen, die die echte Grids-DataSource-Eigenschaft festlegt und ein INotifyPropertyChanged
-Ereignis auslöst - und wenn es eine Nicht-Haupt-Thread-ID ist, dann Debugger.Break();
.
Mein Bauchgefühl ist, dass Sie die Datenquelle eines Controls (z. B. eines Gitters) in einem Hintergrund-Thread (für dieses Nicht-Freeze-Feeling) ändern, was ein Problem bei der Synchronisation verursacht. Dies ist dem anderen DevExpress-Kunden passiert, der dies erlebt hat. Es wird diskutiert aktualisiert hier in einem anderen Thread als den, auf den du dich bezogen hast.
Cross-Thread-Operationen sollten die ganze Zeit in Winforms in die Luft gehen. Es überprüft sie in fast jeder Methode wie verrückt. Für einen Startpunkt sehen Sie sich Ссылка an.
>Irgendwo in Ihrer App könnte jemand diese Codezeile eingefügt haben:
%Vor%Kommentieren Sie das und führen Sie die App aus. Folgen Sie dann den Ausnahmen.
(In der Regel können Sie das Problem beheben, indem Sie das Update in einem Aufruf umbrechen, z. B. in einem Worker-Thread, wenn textbox1.text=SomeString;
in 'textbox.invoke (() = & gt; {textbox1.text = SomeString; }) ;.
Sie müssen möglicherweise auch die Suche nach InvokeRequired hinzufügen, BeginInvoke verwenden, um Deadlocks zu vermeiden, und Werte vom Aufruf zurückgeben, dies sind alles separate Themen.
das geht davon aus, dass selbst ein moderater Refactor nicht in Frage kommt was selbst für eine mittelgroße Unternehmens-App fast immer der Fall ist.
Hinweis: Es ist nicht möglich, eine erfolgreiche Entdeckung dieses Falls durch statische Analyse zu garantieren (dh ohne die App auszuführen). es sei denn, Sie können das Halteproblem lösen ... Ссылка usw.
Ich habe dies getan, um nach dieser speziellen Situation zu suchen, aber natürlich muss ich sie an Ihre Bedürfnisse anpassen, aber der Zweck ist, Ihnen zumindest eine Möglichkeit zu geben.
Ich habe diese Methode SearchForThreads
aufgerufen, aber da es nur ein Beispiel ist, kannst du es so nennen, wie du willst.
Der Grundgedanke besteht darin, diesen Methodenaufruf vielleicht zu einer Basisklasse hinzuzufügen und ihn im Konstruktor aufzurufen, wodurch er etwas flexibler wird.
Verwenden Sie dann reflection, um diese Methode für alle Klassen aufzurufen, die von dieser Basis abgeleitet sind, und eine Ausnahme oder etwas auszulösen, wenn diese Situation in einer Klasse gefunden wird.
Es gibt eine Vorbedingung, nämlich die Verwendung von Framework 4.5.
Diese Version des Frameworks hat das Attribut CompilerServices
hinzugefügt, das uns Details zum Aufrufer der Methode angibt.
Die Dokumentation dafür ist hier
Damit können wir die Quelldatei öffnen und hinein graben.
Was ich getan habe, war, nach der Situation zu suchen, die Sie in Ihrer Frage angegeben haben, indem Sie eine rudimentäre Textsuche verwenden.
Aber es kann Ihnen einen Einblick darüber geben, wie Sie dies bei Ihrer Lösung tun können, da ich sehr wenig über Ihre Lösung weiß, kann ich nur mit dem Code arbeiten, den Sie auf Ihren Beitrag setzen.
%Vor%Ich hoffe, es hilft Ihnen.
Sie können die Zeilennummer erhalten, wenn Sie den Text teilen, damit Sie ihn ausgeben können, aber ich wollte nicht durch die Schwierigkeiten gehen, da ich nicht weiß, was für Sie arbeiten würde.
%Vor%Versuchen Sie das:
%Vor%Dann können Sie die Nachricht und den Aufruf-Stack protokollieren und das sollte Ihnen genügend Informationen geben, um das Problem zu beheben.
Ich empfehle Ihnen, Ihre GUI zu aktualisieren, um diese Situation automatisch zu behandeln. Sie verwenden stattdessen eine Reihe von übernommenen Steuerelementen.
Das allgemeine Prinzip besteht darin, die Eigenschaft Set-Methoden so zu überschreiben, dass sie Thread-sicher wird. Daher wird in jeder überschriebenen Eigenschaft anstelle einer direkten Aktualisierung des Basissteuerelements überprüft, ob ein Aufruf erforderlich ist (dh, wir befinden uns in einem separaten Thread, der GUI). Dann aktualisiert der Invoke-Aufruf die Eigenschaft im GUI-Thread anstelle des sekundären Threads.
Wenn also die geerbten Steuerelemente verwendet werden, kann der Formularcode, der versucht, GUI-Elemente von einem sekundären Thread zu aktualisieren, so belassen werden.
Hier ist das Textfeld und die Schaltflächen. Sie würden bei Bedarf weitere hinzufügen und bei Bedarf weitere Eigenschaften hinzufügen. Anstatt Code auf einzelne Formulare zu setzen.
Sie müssen nicht in den Designer gehen, stattdessen können Sie nur die Designer-Dateien suchen / ersetzen. In ALLEN Designer.cs-Dateien beispielsweise würden Sie System.Windows.Forms.TextBox durch ThreadSafeControls.TextBoxBackgroundThread und System.Windows.Forms.Button durch ThreadSafeControls.ButtonBackgroundThread ersetzen.
Andere Steuerelemente können basierend auf den Steuerarten & amp; Eigenschaften werden vom Hintergrundthread aktualisiert.
%Vor%Tags und Links c# winforms devexpress