Wie werden nicht behandelte Ausnahmen behandelt? (Beenden Sie die Anwendung vs. Keep it alive)

30

Was ist die beste Vorgehensweise, wenn in einer Desktopanwendung nicht behandelte Ausnahmen auftreten?

Ich wollte dem Benutzer eine Nachricht anzeigen, damit er den Support kontaktieren kann. Ich würde dem Benutzer empfehlen, die Anwendung neu zu starten, aber nicht zu erzwingen. Ähnlich wie hier beschrieben: ux.stackexchange.com - Wie gehe ich am besten mit unerwarteten Anwendungsfehlern um?

Das Projekt ist eine .NET WPF-Anwendung, daher könnte der beschriebene Vorschlag folgendermaßen aussehen (Beachten Sie, dass dies ein vereinfachtes Beispiel ist. Wahrscheinlich ist es sinnvoll, die Ausnahmedetails auszublenden, bis der Benutzer auf "Details anzeigen" klickt und einige Funktionen bereitstellt einfach den Fehler melden):

public partial class App : Application
{
    public App()
    {
        DispatcherUnhandledException += OnDispatcherUnhandledException;
    }

    private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
    {
        LogError(e.Exception);
        MessageBoxResult result = MessageBox.Show(
             $"Please help us fix it and contact [email protected]. Exception details: {e.Exception}" +
                        "We recommend to restart the application. " +
                        "Do you want to stop the application now? (Warning: Unsaved data gets lost).", 
            "Unexpected error occured.", MessageBoxButton.YesNo);

        // Setting 'Handled' to 'true' will prevent the application from terminating.
        e.Handled = result == MessageBoxResult.No;
    }

    private void LogError(Exception ex)
    {
        // Log to a log file...
    }
}

In der Implementierung (Commands of ViewModels oder Event-Handler von externen Ereignissen) würde ich dann nur die spezifische exogene Ausnahme abfangen und alle anderen Ausnahmen (ohne Kopf und unbekannte Ausnahmen) bis zum oben beschriebenen "Last-Resort-Handler" sprudeln lassen. Eine Definition von Ausnahmen ohne Kopf und exogenen Ausnahmen finden Sie unter: Eric Lippert - Ärgerliche Ausnahmen

Ist es sinnvoll, den Benutzer entscheiden zu lassen, ob die Anwendung beendet werden soll? Wenn die Anwendung beendet wird, haben Sie mit Sicherheit keinen inkonsistenten Status ... Andererseits kann der Benutzer nicht gespeicherte Daten verlieren oder einen gestarteten externen Prozess nicht mehr stoppen, bis die Anwendung neu gestartet wird.

Oder ist die Entscheidung, ob Sie die Anwendung ausnahmsweise beenden sollten, abhängig von der Art der Anwendung, die Sie schreiben? Ist es nur ein Kompromiss zwischen "Robustheit" und "Korrektheit", wie in Code Complete, Second Edition, beschrieben

Um Ihnen einen gewissen Kontext zu geben, um welche Art von Anwendung es sich handelt: Die Anwendung wird hauptsächlich zur Steuerung chemischer Laborinstrumente und zur Darstellung der Messergebnisse für den Benutzer verwendet. Zu diesem Zweck kommunizieren die WPF-Anwendungen mit einigen Diensten (lokalen und Remote-Diensten). Die WPF-Anwendung kommuniziert nicht direkt mit den Instrumenten.

Jonas Benz
quelle
27
Wenn Sie keine Ausnahme erwartet haben, wie können Sie dann sicher sein, dass die Anwendung weiterhin sicher ist?
Deduplizierer
2
@ Deduplicator: Natürlich können Sie nicht sicher sein. Wie bereits als Kommentar zu Matthews Antwort geschrieben: "Ja, natürlich könnte die Anwendung in einem ungültigen Zustand sein. Vielleicht wurden einige ViewModels nur teilweise aktualisiert. Aber kann dies Schaden anrichten? Der Benutzer kann die Daten neu laden und wenn etwas ungültig wird an den Dienst senden, dann wird der Dienst ihn trotzdem nicht akzeptieren. Ist es nicht besser für den Benutzer, wenn er speichern kann, bevor er die Anwendung neu startet? "
Jonas Benz
2
@Voo Sie stellen also sicher, dass die Anwendung sicher weiterarbeitet, indem Sie immer eine Ausnahme erwarten? Anscheinend lehnen Sie die Annahme einer unerwarteten Ausnahme ab.
Deduplizierer
2
Bitte machen Sie die Fehlermeldung in jedem Fall kopierbar. Alternativ können Sie auch angeben, in welche Protokolldatei sie geschrieben wurde.
ComFreek
2
Die Handhabung impliziert nicht unbedingt eine explizite Aktion. Wenn Sie sicher sind, dass die Anwendung sicher fortgesetzt werden kann, haben Sie die Ausnahme behandelt.
Chepner

Antworten:

47

Sie müssen damit rechnen, dass Ihr Programm aus mehr Gründen als nur einer unbehandelten Ausnahme wie einem Stromausfall oder einem anderen Hintergrundprozess, der das gesamte System zum Absturz bringt, beendet wird. Daher würde ich empfehlen, die Anwendung zu beenden und neu zu starten, aber mit einigen Maßnahmen, um die Folgen eines solchen Neustarts zu mildern und den möglichen Datenverlust zu minimieren .

Beginnen Sie mit der Analyse der folgenden Punkte:

  • Wie viele Daten können bei einem Programmabbruch tatsächlich verloren gehen?

  • Wie schwer ist ein solcher Verlust für den Benutzer wirklich? Können die verlorenen Daten in weniger als 5 Minuten rekonstruiert werden, oder geht es um den Verlust eines Arbeitstages?

  • Wie viel Aufwand ist es, eine "Intermediate Backup" -Strategie umzusetzen? Schließen Sie dies nicht aus, da "der Benutzer bei einem regulären Speichervorgang einen Änderungsgrund eingeben müsste" , wie Sie in einem Kommentar geschrieben haben. Stellen Sie sich lieber so etwas wie eine temporäre Datei oder einen temporären Status vor, die bzw. der nach einem Programmabsturz automatisch neu geladen werden kann. Viele Arten von Produktivitätssoftware tun dies (z. B. MS Office und LibreOffice verfügen beide über eine Funktion zum automatischen Speichern und zur Wiederherstellung nach einem Absturz).

  • Falls die Daten falsch oder fehlerhaft waren, kann der Benutzer dies leicht sehen (möglicherweise nach einem Neustart des Programms)? Wenn ja, können Sie eine Option anbieten, mit der der Benutzer die Daten speichern kann (mit einer geringen Wahrscheinlichkeit, dass sie beschädigt sind), dann einen Neustart erzwingen, neu laden und den Benutzer prüfen lässt, ob die Daten in Ordnung sind. Stellen Sie sicher, dass Sie nicht die letzte Version überschreiben, die regelmäßig gespeichert wurde (schreiben Sie stattdessen an einen temporären Speicherort / in eine temporäre Datei), um eine Beschädigung der alten Version zu vermeiden.

Ob eine solche "Intermediate Backup" -Strategie sinnvoll ist, hängt letztendlich von der Anwendung und ihrer Architektur sowie von der Art und Struktur der beteiligten Daten ab. Aber wenn der Benutzer weniger als 10 Minuten Arbeit verliert und ein solcher Absturz einmal pro Woche oder noch seltener auftritt, würde ich wahrscheinlich nicht zu viel darüber nachdenken.

Doc Brown
quelle
10
en.wikipedia.org/wiki/Crash-only_software , und so funktionieren Android-Apps zwangsläufig.
Mooing Duck
3
Hervorragende Antwort - und ein schönes Beispiel für die Betrachtung von Dingen in einem breiteren Kontext (in diesem Fall "Wie können wir Datenverluste im Falle eines Absturzes verhindern?") Führt zu einer besseren Lösung.
sleske
1
Ich habe eine kleine Änderung vorgenommen, um festzustellen, dass Sie alte Daten nicht überschreiben sollten - ich hoffe, es macht Ihnen nichts aus.
sleske
1
@MooingDuck Viele Android-Apps (z. B. Spiele) verlieren bei einem Absturz ihren Status.
User253751
1
@immibis: Ja, Android hat in der Tat eine große Anzahl von Apps mit wirklich geringer Qualität.
Mooing Duck
30

Dies hängt zu einem gewissen Grad von der Anwendung ab, die Sie entwickeln. Im Allgemeinen würde ich jedoch sagen, dass Sie eine Anwendung beenden müssen, wenn eine nicht behandelte Ausnahme auftritt.

Warum?

Weil Sie dem Status der Anwendung nicht mehr vertrauen können.

Stellen Sie dem Benutzer auf jeden Fall eine hilfreiche Nachricht zur Verfügung, aber Sie sollten die Anwendung letztendlich beenden.

In Anbetracht Ihres Kontexts möchte ich auf jeden Fall, dass die Anwendung beendet wird. Sie möchten nicht, dass Software, die in einem Labor ausgeführt wird, fehlerhafte Ausgaben erzeugt, und da Sie nicht daran gedacht haben, die Ausnahme zu behandeln, wissen Sie nicht, warum sie ausgelöst wurde und was passiert.

Matthew
quelle
Ich habe im letzten Teil versucht, Kontextinformationen zur Anwendung hinzuzufügen.
Jonas Benz
10
@JonasBenz Ist es für den Benutzer nicht besser, wenn er speichern kann, bevor er die Anwendung neu startet? Ja, aber woher wissen Sie, ob die Daten, die der Benutzer speichern würde, gültig und nicht beschädigt sind? Zu diesem Zeitpunkt haben Sie eine unerwartete Ausnahme und Sie wissen wirklich nicht warum. Ihr sicherster, wenn auch für den Benutzer ärgerlicher Weg ist das Beenden der Anwendung. Wenn Sie Bedenken hinsichtlich der Arbeit zum Speichern von Benutzern haben, würden Sie eine Strategie zum konstanten Speichern anwenden. Auch hier hängt alles von der Anwendung ab, die Sie schreiben.
Matthew
4
Ja, ich kann hier genauso argumentieren: Ich bin mit dem Vorhandensein eines Weiter-Buttons nicht einverstanden. Das Problem ist einfach, dass der Anwendungsentwickler nicht weiß, ob Sie sicher weitermachen können. Wie kann der Benutzer das wissen? Wenn Sie eine unbehandelte Ausnahme erhalten, bedeutet dies, dass Sie einen Fehler haben, den Sie nicht erwartet haben, und Sie können nicht sicher sagen, was zu diesem Zeitpunkt passiert. Der Benutzer möchte fortfahren, weil er seine Arbeit nicht verlieren möchte. Ich verstehe das. Möchten Sie ihn jedoch fortfahren lassen, auch wenn Ihre Anwendung aufgrund dieses Fehlers möglicherweise schlechte Ergebnisse liefert?
Matthew
3
@Matthew "Wenn Sie, der Anwendungsentwickler, nicht wissen, ob Sie sicher fortfahren können, wie kann der Benutzer es wissen" , wusste der Entwickler nicht, wann er den Code geschrieben hat. Wenn der Benutzer auf einen bestimmten Fehler wie diesen stößt, ist dieser möglicherweise bekannt. Der Benutzer kann dies in den jeweiligen Benutzerforen, Support-Kanälen und so weiter herausfinden oder einfach testen und sehen, was mit seinen Daten passiert. Ich stimme zu, dass die Benutzerfunktion immer noch etwas zu dunkel und gefährlich ist es ist dem Benutzer möglich, in der Tat zu wissen, ob "Fortfahren" sinnvoll ist oder nicht.
Hyde
1
@JonasBenz unter Windows 3.1 enthielt das Dialogfeld, das angezeigt wurde, als ein Programm einen ungültigen Speicherzugriff ausführte, eine Schaltfläche "Ignorieren", mit der das Programm weiter ausgeführt werden konnte. Sie werden feststellen, dass jede nachfolgende Windows-Version nicht über diese Schaltfläche verfügt.
Mark
12

In Anbetracht dessen, dass dies für ein chemisches Labor gedacht ist und Ihre Anwendung die Instrumente nicht direkt, sondern über andere Dienste steuert:

Beenden erzwingen, nachdem die Nachricht angezeigt wurde. Nach einer nicht behandelten Ausnahme befindet sich Ihre Anwendung in einem unbekannten Zustand. Es können fehlerhafte Befehle gesendet werden. Es kann sogar Nasendämonen hervorrufen . Ein fehlerhafter Befehl kann möglicherweise teure Reagenzien verschwenden oder die Ausrüstung oder Personen gefährden .

Sie können jedoch noch etwas anderes tun: Stellen Sie sich nach dem Neustart ordnungsgemäß wieder her . Ich gehe davon aus, dass Ihre Anwendung diese Hintergrunddienste nicht selbst herunterfährt, wenn sie abstürzt. In diesem Fall können Sie den Status problemlos wiederherstellen. Wenn Sie mehr Status haben, können Sie diesen auch speichern. In einem Speicher, der Bestimmungen für die Atomizität und Integrität von Daten enthält (SQLite vielleicht?).

Bearbeiten:

Wie in den Kommentaren angegeben, erfordert der von Ihnen gesteuerte Prozess möglicherweise Änderungen, die schnell genug sind, damit der Benutzer keine Zeit zum Reagieren hat. In diesem Fall sollten Sie zusätzlich zur ordnungsgemäßen Wiederherstellung des Status einen unbeaufsichtigten Neustart der App in Betracht ziehen.

Jan Dorniak
quelle
Die Beendigung in einem Zustand, in dem NACHFOLGEBEFEHLE JETZT erforderlich sind, kann im Chemielabor genauso gefährlich sein.
Oleg V. Volkov
@ OlegV.Volkov also vielleicht selbst bei Kündigung neu starten? Auf einem anständigen Computer sollte das Starten einer GUI Hunderte von Millisekunden dauern. Wenn der Prozess härtere Timings erfordert, würde die Steuerung auf einem Nicht-Echtzeitbetriebssystem nicht implementiert. Obwohl es das OP ist, das die endgültige Risikobewertung vornehmen sollte.
Jan Dorniak
@ OlegV.Volkov das ist aber ein guter Punkt, deshalb habe ich meine Meinung dazu in der Antwort hinzugefügt.
Jan Dorniak
8

Der Versuch, diese Frage auf der obersten Ebene des Programms zu beantworten, ist kein kluges Spiel.

Wenn irgendetwas den ganzen Weg in die Luft gesprudelt ist und zu keinem Zeitpunkt in der Architektur der Anwendung über diesen Fall nachgedacht wurde, haben Sie keine Verallgemeinerungen darüber, welche Aktionen sicher sind oder nicht.

Nein, es ist definitiv kein allgemein akzeptables Design, dem Benutzer die Wahl zu lassen, ob die Anwendung wiederhergestellt werden soll oder nicht, da die Anwendung und die Entwickler nachweislich nicht die erforderliche Sorgfalt aufgewendet haben, um herauszufinden, ob dies möglich oder überhaupt sinnvoll ist .

Verfügt die Anwendung jedoch über wichtige Teile ihrer Logik oder ihres Verhaltens, die für diese Art der Fehlerbehebung entwickelt wurden, und können diese in diesem Fall auf jeden Fall genutzt werden - in diesem Fall kann es akzeptabel sein, den Benutzer aufzufordern, zu sehen, ob er einen Wiederherstellungsversuch unternehmen möchte, oder ob er ihn einfach beenden und von vorne beginnen möchte.

Diese Art der Wiederherstellung ist im Allgemeinen nicht für alle (oder sogar die meisten) Programme erforderlich oder empfehlenswert. Wenn Sie jedoch an einem Programm arbeiten, für das dieses Maß an Betriebsintegrität erforderlich ist, kann dies ein Umstand sein, unter dem diese Art von a angezeigt wird Aufforderung an einen Benutzer wäre eine vernünftige Sache zu tun.

In Abwesenheit einer speziellen Fehlerbehebungslogik - Nein, tun Sie dies nicht. Sie haben buchstäblich keine Ahnung, was passieren wird, wenn Sie dies getan hätten, hätten Sie die Ausnahme weiter unten abgefangen und damit umgegangen.

Eiserner Gremlin
quelle
Leider unterscheiden viele Methoden für Dinge wie "Erstellen eines Objekts mit Daten, die von einem bestimmten Ort empfangen wurden" nicht zwischen Ausnahmen, die darauf hinweisen, dass die Aktion nicht abgeschlossen werden konnte, aber der Versuch hatte keine Nebenwirkungen im Vergleich zu solchen, die auf etwas Schwerwiegenderes hinweisen ist schief gelaufen. Die Tatsache, dass ein Versuch, eine Ressource zu laden, aus irgendeinem Grund fehlgeschlagen ist, den man nicht erwartet hatte, sollte keinen schwerwiegenden Fehler erzwingen, wenn man im Allgemeinen auf die Unfähigkeit vorbereitet ist, das Objekt zu konstruieren. Was zählt, sind Nebenwirkungen, die leider von Ausnahmeframeworks ignoriert werden.
Supercat
@supercat - Wenn Sie einen Fehler identifizieren können, können Sie ihn behandeln. Wenn Sie es nicht identifizieren können, können Sie es nicht handhaben, es sei denn, Sie schreiben eine Routine, um den Status der Anwendung auf Integrität zu überprüfen, um herauszufinden, was möglicherweise schief gelaufen ist. Es spielt keine Rolle, wie der Fehler "hätte sein können". Wir haben ausdrücklich festgestellt, dass wir das nicht wissen, weil wir versuchen, im Allgemeinen mit nicht erfassten Ausnahmen umzugehen.
Iron Gremlin
3

Das Problem mit "außergewöhnlichen Ausnahmen", dh Ausnahmen, die Sie nicht vorausgesehen haben, besteht darin, dass Sie nicht wissen, in welchem ​​Status sich das Programm befindet. Wenn Sie beispielsweise versuchen, die Daten des Benutzers zu speichern , können sogar noch mehr Daten zerstört werden .

Aus diesem Grund sollten Sie die Anwendung beenden.

Es gibt eine sehr interessante Idee namens Crash-only Software von George Candea und Armando Fox . Die Idee ist, dass, wenn Sie Ihre Software so entwerfen, dass Sie sie nur schließen können, indem Sie sie abstürzen, und wenn Sie sie nur starten können, indem Sie sie nach einem Absturz wiederherstellen, Ihre Software ausfallsicherer wird und der Fehler behoben wird Codepfade werden viel gründlicher getestet und ausgeführt.

Sie kamen auf diese Idee, nachdem sie festgestellt hatten, dass einige Systeme nach einem Absturz schneller starteten als nach einem ordnungsgemäßen Herunterfahren.

Ein gutes, aber nicht mehr relevantes Beispiel sind einige ältere Versionen von Firefox, die nicht nur schneller starten, wenn sie sich von einem Absturz erholen, sondern auch ein besseres Starterlebnis haben ! Wenn Sie Firefox in diesen Versionen normal beenden, werden alle geöffneten Registerkarten geschlossen und mit einer einzelnen leeren Registerkarte gestartet. Bei der Wiederherstellung nach einem Absturz werden die zum Zeitpunkt des Absturzes geöffneten Registerkarten wiederhergestellt. (Und das war die einzige Möglichkeit, Firefox zu schließen, ohne den aktuellen Browserkontext zu verlieren.) Also, was haben die Leute getan? Sie haben Firefox einfach nie geschlossen und stattdessen immer pkill -KILL firefoxbearbeitet.

Es ist eine nette Zuschreibung über Crash-only - Software von Valerie Aurora auf Linux Weekly News . Die Kommentare sind auch eine Lektüre wert. Zum Beispiel weist jemand in den Kommentaren zu Recht darauf hin, dass diese Ideen nicht neu sind und tatsächlich mehr oder weniger den Entwurfsprinzipien von Erlang / OTP-basierten Anwendungen entsprechen. Und wenn wir uns das heute, weitere 10 Jahre nach Valerie und 15 Jahre nach dem ursprünglichen Artikel, ansehen, stellen wir möglicherweise fest, dass der aktuelle Micro-Service-Hype diese Ideen noch einmal neu erfindet. Das moderne Cloud-Rechenzentrumsdesign ist auch ein Beispiel für eine gröbere Granularität. (Jeder Computer kann jederzeit abstürzen, ohne das System zu beeinträchtigen.)

Es reicht jedoch nicht aus, Ihre Software zum Absturz zu bringen. Es muss dafür ausgelegt sein. Idealerweise wird Ihre Software in kleine, unabhängige Komponenten aufgeteilt, die jeweils unabhängig voneinander abstürzen können. Außerdem sollte sich der "Absturzmechanismus" außerhalb der abgestürzten Komponente befinden.

Jörg W. Mittag
quelle
1

Die ordnungsgemäße Behandlung der meisten Ausnahmen sollte darin bestehen, Objekte, die sich möglicherweise in einem beschädigten Zustand befinden, für ungültig zu erklären und die Ausführung fortzusetzen, wenn ungültige Objekte dies nicht verhindern. Das sichere Paradigma zum Aktualisieren einer Ressource wäre beispielsweise:

acquire lock
try
  update guarded resource
if exception
  invalidate lock
else
  release lock
end try

Wenn beim Aktualisieren der geschützten Ressource eine unerwartete Ausnahme auftritt, sollte davon ausgegangen werden, dass die Ressource beschädigt und die Sperre ungültig ist, unabhängig davon, ob die Ausnahme von einem Typ ist, der ansonsten harmlos wäre.

Leider werden Ressourcenschutzeinrichtungen, die über IDisposable/ implementiert wurden using, immer dann freigegeben, wenn der geschützte Block beendet wird, ohne dass bekannt ist, ob der Block normal oder abnormal beendet wurde. Obwohl es genau definierte Kriterien geben sollte, wann nach einer Ausnahme fortgefahren werden soll, gibt es keine Möglichkeit zu sagen, wann sie zutreffen.

Superkatze
quelle
+1 nur, um diese relativ nicht offensichtliche und immer noch unübliche Perspektive auf den richtigen Weg auszudrücken. Ich weiß eigentlich noch nicht, ob ich damit einverstanden bin, da dies für mich eine neuartige Heuristik / Regel ist, also muss ich eine Weile darüber nachdenken, aber es scheint plausibel.
mtraceur
0

Sie können den Ansatz verwenden, den jede einzelne iOS- und MacOS-App verfolgt: Eine nicht abgefangene Ausnahme beendet die Anwendung sofort. Plus viele Fehler, wie Array out of bounds oder nur arithmetischer Überlauf in neueren Anwendungen, tun dasselbe. Keine Warnung.

Nach meiner Erfahrung nehmen viele Benutzer keine Notiz, sondern tippen erneut auf das App-Symbol.

Natürlich müssen Sie sicherstellen, dass ein solcher Absturz nicht zu erheblichen Datenverlusten und definitiv nicht zu kostspieligen Fehlern führt. Aber eine Warnung: „Ihre App stürzt jetzt ab. Rufen Sie den Support an, wenn es Sie stört. “Hilft niemandem.

gnasher729
quelle