Frage

Ich habe meine erste MVVM -Anwendung geschrieben. Wenn ich die Anwendung schließe, bekomme ich oft einen Absturz, der durch eine ObjectDisposedException ausgestattet bin. Der Absturz erscheint, wenn die Anwendung stirbt, kurz nachdem das App -Fenster verschwunden ist.

Eine Stacktrace zu bekommen war schwierig (Siehe meine andere Frage), aber schließlich habe ich und stellte fest, dass mein Stacktrace vollständig in C# -Bibliotheken enthalten ist (Kernel32! Basethreadstart, mscorwks! Thread, mscorwks! Wks usw.).

Darüber hinaus ist dieser Absturz inkonsistent. Nach meiner letzten Kasse und Wiederaufbau hörte es auf ... für eine Weile. Dann kam es zurück. Sobald es passiert, passiert es weiter, Auch wenn ich "putze" und wieder aufbauen ". Aber ein Wisch- und Überprüfungsvorgang lässt es manchmal für eine Weile aufhören.

Was ich denke, passiert:

Ich denke, der Garecollector macht etwas Lustiges, wenn ich meine ViewModels entsetzt. Meine ViewModelBase -Klasse Destructor hat eine Schreiblinie zum Log Sequenz, aber mein Kollege sieht eine andere Sequenz mit verschiedenen Objekten, die angeordnet sind).

Da die Stacktrace keinen der Anrufe meines Codes enthält, denke ich, dass dies nicht der Fall ist mein Code, der die Methode eines entsorgten Objekts aufruft. Das lässt mich denken, dass die CLR dumm ist.

Macht das Sinn? Gibt es eine Möglichkeit, dass ich den GC konsistent machen kann? Ist das ein roter Hering?

Andere Details, die helfen könnten:
Alle meine Ansichten und ViewModels werden in meiner Start -Event -Event -Handler der Anwendung von App.xaml.cs Datei erstellt. Der gleiche Handler weist den DataContext ViewModels zu. Ich bin mir nicht sicher, ob dies die richtige MVVM -Praxis ist (wie gesagt, meine erste MVVM -App), aber ich verstehe nicht, warum dies schlechtes Verhalten verursachen würde.

Ich kann bei Bedarf Code einfügen.

War es hilfreich?

Lösung

Ihre Bewerbung wirft eine Ausnahme aus, da Ihre Protokollierungsaktion in der ViewModel -Zerstörung nicht abgeschlossen ist, wenn Ihre Hauptanwendung beendet ist.

Sie werden feststellen, dass die tatsächliche Datei, die einen untergeordneten Prozess schreibt, hervorgerufen wird. Wenn dies nicht abgeschlossen ist, wenn Ihre Hauptanwendung beendet ist, erhalten Sie einen Fehler.

Wenn Sie diese Art von Aktion ausführen möchten, benötigen Sie Ihre Hauptanwendung, um einen Zeitraum für untergeordnete Prozesse/Threadpool -Threads usw. zu warten, um vor dem Abschluss zu vervollständigen.

Wenn Sie sicherstellen möchten, dass Sie Ereignisse protokollieren können, die während Ihres Antragsabschlusses stattfinden, empfehle ich Ihnen, Ihren Protokollierungsprozess (das tatsächliche Schreiben in Ihre Protokolldatei) als separate primäre Thread auszuführen, auf den Sie Nachrichten veröffentlichen. Auf diese Weise kann Ihre Bewerbung geschlossen werden, bevor Ihr Protokollierungsprozess das Schreiben an die Festplatte abgeschlossen hat.

Andere Tipps

Meine ViewModelBase -Klasse Destructor hat eine Schreiblinie zu protokollieren, wenn der Destruktor aufgerufen wird.

Das ist wirklich schlimm. Ich hoffe, Sie haben das nur im Debug -Build aktiviert.

Sie sollten unbedingt nicht je Machen Sie in einem Zerstörer alles Komplizierte, wie das Erstellen von Dateihandles, die Manipulation des Zustands der Datenträger usw. Das bittet nur um Schwierigkeiten der schlimmsten Art. Ein Destruktor sollte eine nicht verwaltete Ressource aufräumen und absolut nichts anderes tun.

Von meinen 4 ViewModels werden nur 2 oder 3 entsorgt, und es scheint abhängig von der Kasse zu variieren (z. B. wenn ich sie auf meinem ausführe, sehe ich eine konsequent wiederholte Sequenz, aber mein Kollege sieht eine andere Sequenz mit verschiedenen Objekten angeordnet).

Das Sie sehen, dass Dinge zu unterschiedlichen Zeiten in verschiedenen Bestellungen passieren, ist, wie wir weiter unten sehen werden, ganz zu erwarten.

Einen Zerstörer richtig zu schreiben ist eines der schwierigsten Dinge in C#; Dass Sie während der letzten Abschlussrunde eine Ausnahme erhalten, bevor der Prozess abgeschaltet wird, zeigt, dass Sie es wahrscheinlich falsch machen.

Das lässt mich denken, dass die CLR dumm ist.

Es ist unwahrscheinlich, dass das Tool für Ihren Fehler die Behebung Ihres Problems hilft.

Dinge, die jeder wissen sollte, bevor sie einen Destruktor schreiben, sind:

  • Destruktoren werden nicht unbedingt auf demselben Thread wie jeder andere Code ausgeführt. Das bedeutet, dass Sie möglicherweise Rennbedingungen haben, Probleme mit der Sperrordnung, liest und schreibt aufgrund schwacher Speichermodelle und so weiter. Wenn Sie Destruktoren verwenden, schreiben Sie automatisch ein Multithread -Programm und müssen daher das Programm entwerfen, um gegen alle möglichen Threading -Probleme zu verteidigen. Das ist dein Verantwortung, nicht die Verantwortung des CLR. Wenn Sie nicht bereit sind, die Verantwortung für das Schreiben eines Threadsafe -Objekts zu übernehmen Schreiben Sie keinen Destruktor.

  • Destruktoren laufen auch, auch wenn das Objekt nie initialisiert wurde. Es ist durchaus möglich, dass nach einem Objekt zugewiesen wurde und der Code in der Mitte eines Konstruktors eine Ausnahme ausgelöst wird. Das Objekt wird zugewiesen, Sie haben die Abschluss nicht unterdrückt und daher muss es zerstört werden. Ein Destruktor muss angesichts eines unvollständig initialisierten Objekts robust sein.

  • Wenn ein Objekt unter einem Schloss steht, um eine konsistente Mutation zu gewährleisten, und eine Ausnahme ausgelöst wird, und der schließlich den endgültigen Block nicht den konsistenten Zustand wiederherstellt, wird das Objekt bei der Fertigstellung in einem inkonsistenten Zustand sein. Destruktoren müssen angesichts von Objekten mit inkonsistenten inneren Zustand aufgrund abgebrochener Transaktionen robust sein.

  • Zerstörer können hereinlaufen jede Bestellung. Wenn Sie einen Baum von Objekten haben, die sich alle aufeinander beziehen und gleichzeitig tot sind, können die Zerstörer für jedes Objekt jederzeit ausgeführt werden. Zerstörer müssen angesichts von Objekten, deren interner Zustand sich auf andere Objekte bezieht, die nicht nur zerstört wurden oder nicht, robust sein.

  • Objekte, die auf die Zerstörung in der Finalizer -Warteschlange warten, sind am Leben Nach dem Müllsammler. Ein Destruktor bewirkt, dass ein bisher totes Objekt vorübergehend (wir hoffen!) Wieder lebendig werden. Wenn Ihre Programmlogik von toten Objekten abhängt, die tot bleiben, müssen Sie mit Ihren Zerstörern sehr vorsichtig sein. (Und wenn die Destruktor -Logik das Objekt wieder dauerhaft lebendig macht, haben Sie möglicherweise ein großes Problem in Ihren Händen. Tun Sie das nicht.)

  • Weil Objekte, die auf Zerstörung warten, sind am Leben, und sie werden als Zerstörung identifiziert, weil der GC sie als klassifizierte als tot, Ein Objekt, das auf die Fertigstellung wartet. Dies bedeutet, dass die Rückgewinnung der Lagerung durch den Müllsammler nicht auftreten kann, wenn das Objekt für die tot ist zweite Zeit. Da sich das Objekt gerade zu einer späteren Generation bewegt, ist dies möglicherweise nicht lange festgelegt. Zerstörer führen dazu, dass kurzlebige Gedächtniszuweisungen viel länger gelebt werden, was die Leistung des Müllsammlers in einigen Szenarien ernsthaft beeinflussen kann. Denken Sie sehr sorgfältig nach, bevor Sie einen Destruktor für ein großes, kurzlebiges Objekt schreiben (oder schlimmer noch ein kleines kurzlebiges Objekt, das Sie Millionen machen werden); Objekte mit Zerstörern können nicht vom Gen -Zero -Sammler befreit werden, es sei denn.

  • Zerstörer sind nicht garantiert gerufen werden. Der Müllsammler ist nicht benötigt Destruktoren eines Objekts zu führen, bevor der Prozess abgeschaltet wird, auch wenn sie als tot bekannt sind. Ihre Logik kann nicht abhängen für seine Korrektheit der Zerstörer, die gerufen werden. Viele Dinge können verhindern, dass Zerstörer aufgerufen werden - zum Beispiel eine Ausnahme von Stapelüberlauf oder jemand, der das Netzkabel aus der Wand zieht. Die Programme müssen angesichts von Destruktoren, die niemals gerufen werden, robust sein.

  • Zerstörer, die unberatte Ausnahmen werfen, bringen den Prozess in einen gefährlichen Zustand. Die Laufzeit -Engine ist ausschließlich in seinem Rechte, den gesamten Prozess zu fälschen, fehlgeschlagen, wenn dies geschieht. (Obwohl es nicht ist erforderlich dies tun.) Ein Destruktor darf niemals eine ungehandelte Ausnahme auswerfen.

Wenn Sie nicht bereit sind, mit diesen Beschränkungen zu leben Schreiben Sie in erster Linie keinen Destruktor. Diese Einschränkungen verschwinden nicht, ob Sie sie mögen oder nicht.

Ich denke, der Garecollector macht etwas Lustiges, wenn ich meine ViewModels entsetzt. Meine ViewModelBase -Klasse Destructor hat eine Schreiblinie zu protokollieren, wenn der Destruktor aufgerufen wird

Das ist wahrscheinlich das Problem genau dort. Sie sollten Finalizers überhaupt nicht verwenden, es sei denn, Sie haben wirklich einen guten Grund dafür, und das Protokollieren von Sachen ist definitiv keiner von ihnen.

Sie müssen verstehen, dass Finalizer nicht in einer vorhersehbaren Reihenfolge laufen. Der GC kann die Finalizer anrufen, wenn und in der Reihenfolge, die er will, wahrscheinlich erklärt, warum Sie scheinbar zufälliges Ausnahmeverhalten erhalten.

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