Come trattare le eccezioni non gestite? (Termina l'applicazione vs. Keep it alive)


30

Qual è la migliore pratica quando si verificano eccezioni non gestite in un'applicazione desktop?

Stavo pensando di mostrare un messaggio all'utente, in modo che potesse contattare l'assistenza. Vorrei raccomandare all'utente di riavviare l'applicazione, ma non forzarlo. Simile a ciò che è discusso qui: ux.stackexchange.com - Qual è il modo migliore per gestire errori imprevisti delle applicazioni?

Il progetto è un'applicazione .NET WPF, quindi la proposta descritta potrebbe apparire così (Notare che questo è un esempio semplificato. Probabilmente avrebbe senso nascondere i dettagli dell'eccezione finché l'utente non fa clic su "Mostra dettagli" e fornisce alcune funzionalità a segnala facilmente l'errore):

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 support@example.com. 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...
    }
}

Nell'implementazione (Comandi di ViewModels o gestore di eventi di eventi esterni) catturerei quindi solo l'eccezione esogena specifica e lascerei che tutte le altre eccezioni (eccezioni non comuni) rimandino al "Gestore dell'ultima risorsa" descritto sopra. Per una definizione di eccezioni stordite ed esogene dare un'occhiata a: Eric Lippert - Eccezioni molestanti

Ha senso lasciare che l'utente decida se l'applicazione deve essere chiusa? Quando l'applicazione viene terminata, sicuramente non si ha uno stato incoerente ... D'altra parte l'utente può perdere dati non salvati o non è più in grado di interrompere qualsiasi processo esterno avviato fino al riavvio dell'applicazione.

O è la decisione se è necessario chiudere l'applicazione per eccezioni non gestite a seconda del tipo di applicazione che si sta scrivendo? È solo un compromesso tra "robustezza" e "correttezza" come descritto in Codice completo, Seconda edizione

Per darti un contesto di quale tipo di applicazione stiamo parlando: l'applicazione viene utilizzata principalmente per controllare gli strumenti di laboratorio chimico e mostrare all'utente i risultati misurati. Per fare ciò le applicazioni WPF comunicano con alcuni servizi (servizi locali e remoti). L'applicazione WPF non comunica direttamente con gli strumenti.


27
Se non ti aspettavi un'eccezione, come puoi essere sicuro che l'applicazione possa continuare a spostarsi in sicurezza?
Deduplicatore

2
@Deduplicator: certo, non puoi esserne sicuro. Come già scritto come commento alla risposta di Matthew : "Sì, certo che l'applicazione potrebbe essere in uno stato non valido. Forse alcuni ViewModel sono stati aggiornati solo parzialmente. Ma questo può fare del male? L'utente può ricaricare i dati e se qualcosa di non valido sarà inviare al servizio, quindi il servizio non lo accetterà comunque. Non è meglio per l'utente se è in grado di salvare prima di riavviare l'applicazione? "
Jonas Benz,

2
@Voo Quindi, ti assicuri che l'applicazione possa continuare tranquillamente aspettandoti sempre un'eccezione? Sembra che tu stia negando la premessa di ricevere un'eccezione imprevista.
Deduplicatore

2
In ogni caso, rendere il messaggio di errore copiabile. In alternativa, dire in quale file di registro è stato scritto.
ComFreek

2
La gestione non implica necessariamente un'azione esplicita. Se puoi essere sicuro che l'applicazione possa continuare in modo sicuro, hai gestito l'eccezione.
Chepner

Risposte:


47

Devi aspettarti che il tuo programma si interrompa per più motivi che non solo per un'eccezione non gestita, come un'interruzione dell'alimentazione o un diverso processo in background che causa l'arresto anomalo dell'intero sistema. Pertanto, consiglierei di terminare e riavviare l'applicazione, ma con alcune misure per mitigare le conseguenze di tale riavvio e minimizzare la possibile perdita di dati .

Inizia con l'analisi dei seguenti punti:

  • Quanti dati possono effettivamente andare persi in caso di interruzione del programma?

  • Quanto è grave una perdita del genere per l'utente? I dati persi possono essere ricostruiti in meno di 5 minuti o stiamo parlando di perdere una giornata di lavoro?

  • Quanto sforzo è necessario per attuare una strategia di "backup intermedio"? Non escluderlo perché "l'utente dovrebbe inserire un motivo di modifica" in una normale operazione di salvataggio, come hai scritto in un commento. Meglio pensare a qualcosa come un file o uno stato temporaneo, che può essere ricaricato automaticamente dopo un arresto anomalo del programma. Lo fanno molti tipi di software di produttività (ad esempio, MS Office e LibreOffice dispongono entrambi di una funzione di "salvataggio automatico" e di ripristino da crash).

  • Nel caso in cui i dati fossero errati o danneggiati, l'utente può vederlo facilmente (forse dopo un riavvio del programma)? In caso affermativo, potresti offrire un'opzione per consentire all'utente di salvare i dati (con qualche piccola possibilità che sia danneggiato), quindi forzare un riavvio, ricaricarlo e consentire all'utente di verificare se i dati sembrano corretti. Assicurati di non sovrascrivere l'ultima versione salvata regolarmente (invece scrivi in ​​un percorso / file temporaneo) per evitare di corrompere la versione precedente.

Se tale strategia di "backup intermedio" è un'opzione ragionevole dipende in definitiva dall'applicazione e dalla sua architettura, nonché dalla natura e dalla struttura dei dati coinvolti. Ma se l'utente perderà meno di 10 minuti di lavoro, e un tale incidente si verifica una volta alla settimana o anche più di rado, probabilmente non investirei troppo pensiero in questo.


10
it.wikipedia.org/wiki/Crash-only_software , ed è così che le app Android funzionano per necessità.
Mooing Duck

3
Ottima risposta - e un bell'esempio su come considerare le cose in un contesto più ampio (in questo caso "come possiamo prevenire la perdita di dati in ogni caso di crash?") Porta a una soluzione migliore.
sleske

1
Ho fatto una piccola modifica per notare che non dovresti sovrascrivere i vecchi dati - spero che non ti dispiaccia.
sleske

1
@MooingDuck Molte app Android (come i giochi) perdono il loro stato in caso di arresto anomalo.
user253751

1
@immibis: Sì, Android ha davvero un gran numero di app di qualità davvero bassa.
Mooing Duck

30

Dipende in parte dall'applicazione che stai sviluppando, ma in generale, direi che se la tua applicazione incontra un'eccezione non gestita, devi terminarla.

Perché?

Perché non puoi più avere fiducia nello stato dell'applicazione.

Sicuramente, fornisci un messaggio utile all'utente, ma alla fine dovresti chiudere l'applicazione.

Dato il tuo contesto, vorrei sicuramente che l'applicazione terminasse. Non si desidera che il software in esecuzione in un laboratorio produca output danneggiati e poiché non si è pensato di gestire l'eccezione, non si ha idea del perché sia ​​stato generato e di ciò che sta accadendo.


Ho provato ad aggiungere alcune informazioni di contesto sull'applicazione nell'ultima parte.
Jonas Benz,

10
@JonasBenz Non è meglio per l'utente se è in grado di salvare prima di riavviare l'applicazione? Sì, ma come fai a sapere se i dati che l'utente sta salvando sono validi e non corrotti? A questo punto, hai un'eccezione inaspettata e non sai davvero perché. Il tuo percorso più sicuro, anche se fastidioso per l'utente, è quello di chiudere l'applicazione. Se sei preoccupato per il salvataggio del lavoro da parte dell'utente, utilizzeresti una strategia a risparmio costante. Ancora una volta, tutto dipende dall'applicazione che stai scrivendo.
Matteo,

4
Sì, posso discutere allo stesso modo qui: non sono d'accordo con la presenza di un pulsante Continua. Il problema è semplicemente che se tu, lo sviluppatore dell'applicazione, non sai se puoi continuare in sicurezza, come può sapere l'utente? Se ricevi un'eccezione non gestita, significa che hai un errore che non ti aspettavi e che non puoi dire con certezza cosa sta succedendo a questo punto. L'utente vorrà continuare perché non vorrà perdere il proprio lavoro, lo capisco, ma vuoi lasciarlo continuare anche se la tua applicazione potrebbe produrre risultati negativi a causa di questo errore?
Matteo

3
@Matthew "se tu, lo sviluppatore dell'applicazione non sai se puoi continuare in sicurezza, come può sapere l'utente" , lo sviluppatore non sapeva quando hanno scritto il codice. Quando l'utente rileva un bug specifico come questo, può essere noto. E l'utente può scoprire da qualunque forum di utenti, canali di supporto e quant'altro, o semplicemente testando e vedendo cosa succede ai loro dati ... Sono d'accordo che è ancora un po 'oscuro e pericoloso come funzionalità utente, sottolineando che il tempo lo rende possibile per l'utente effettivamente sapere se "continua" è ragionevole o meno.
hyde

1
@JonasBenz, in Windows 3.1, la finestra di dialogo che appariva quando un programma eseguiva un accesso illegale alla memoria aveva un pulsante "ignora" che consentiva al programma di continuare a funzionare. Noterai che ogni versione successiva di Windows non ha quel pulsante.
Segna il

12

Considerando che questo è pensato per un laboratorio chimico e che la tua applicazione non controlla direttamente gli strumenti ma piuttosto attraverso altri servizi:

Forza la terminazione dopo aver mostrato il messaggio. Dopo un'eccezione non gestita, l'applicazione si trova in uno stato sconosciuto. Potrebbe inviare comandi errati. Può persino invocare demoni nasali . Un comando errato potrebbe potenzialmente sprecare reagenti costosi o mettere in pericolo apparecchiature o persone .

Ma puoi fare qualcos'altro: ripristina con grazia dopo il riavvio . Suppongo che la tua applicazione non abbassi quei servizi in background con se stessa quando si blocca. In tal caso è possibile recuperare facilmente lo stato da essi. Oppure, se hai più stato, considera di salvarlo. In una memoria che ha disposizioni per atomicità e integrità dei dati (forse SQLite?).

Modificare:

Come indicato nei commenti, il processo che controlli potrebbe richiedere modifiche abbastanza rapidamente che l'utente non avrà il tempo di reagire. In tal caso, dovresti prendere in considerazione il riavvio silenzioso dell'app oltre al ripristino grazioso dello stato.


La risoluzione in uno stato che richiede comandi di follow-up ADESSO potrebbe essere altrettanto pericolosa in laboratorio chimico.
Oleg V. Volkov,

@ OlegV.Volkov quindi forse riavviarti al termine? Su un computer decente l'avvio di una GUI dovrebbe assumere l'ordine di centinaia di millisecondi in alto. Se il processo richiede tempi più difficili, il controllo non verrebbe implementato su un sistema operativo non in tempo reale. Sebbene sia l'OP che dovrebbe effettuare la valutazione finale del rischio.
Jan Dorniak,

@ OlegV.Volkov è un buon punto, quindi ho aggiunto la mia opinione su di esso nella risposta.
Jan Dorniak,

8

Cercare di rispondere generalmente a questa domanda ai massimi livelli del programma non è un gioco intelligente.

Se qualcosa è gorgogliato fino in fondo, e in nessun caso nell'architettura dell'applicazione qualcuno ha preso in considerazione questo caso, non hai generalizzazioni che puoi fare su quali azioni sono o non sono sicure da intraprendere.

Quindi, no, non è sicuramente un progetto generalmente accettabile consentire all'utente di scegliere se l'applicazione tenta di recuperare o meno, poiché l'applicazione e gli sviluppatori non hanno dimostrato la dovuta diligenza necessaria per scoprire se ciò è possibile o addirittura saggio .

Tuttavia, se l'applicazione ha parti di alto valore della sua logica o comportamento che sono stati progettati tenendo presente questo tipo di recupero degli errori, ed è possibile sfruttarli in questo caso, quindi, in ogni caso, fallo - In quel caso , può essere accettabile chiedere all'utente di vedere se desidera tentare il ripristino o se desidera semplicemente chiamarlo e uscire e ricominciare.

Questo tipo di recupero non è generalmente necessario o consigliabile per tutti (o anche la maggior parte) dei programmi, ma, se si sta lavorando a un programma per il quale è richiesto questo grado di integrità operativa, potrebbe essere una circostanza in cui presentare questo tipo di sollecitare un utente sarebbe una cosa sensata da fare.

In leiu di qualsiasi logica di recupero di errore speciale - No, non farlo. Non hai letteralmente idea di cosa accadrà, se lo facessi, avresti colto ulteriormente l'eccezione e gestito.


Sfortunatamente, molti metodi per cose come "Costruisci un oggetto con i dati ricevuti da una certa posizione" non fanno distinzione tra eccezioni che indicano che l'azione non può essere completata, ma il tentativo non ha avuto effetti collaterali, rispetto a quelli che indicano che qualcosa di più serio è andato storto. Il fatto che un tentativo di caricare una risorsa sia fallito per qualche motivo che non ci si aspettava non dovrebbe forzare un errore fatale se si è generalmente preparati all'incapacità di costruire l'oggetto. Ciò che conta sono gli effetti collaterali, che sfortunatamente è qualcosa che i quadri delle eccezioni ignorano.
supercat

@supercat - Se riesci a identificare un errore, puoi gestirlo. Se non riesci a identificarlo, non puoi gestirlo, a meno che tu non scriva una routine per eseguire un controllo di integrità sullo stato dell'applicazione per provare a scoprire cosa potrebbe essere andato storto. Non importa quale sia stato l'errore "avrebbe potuto essere", abbiamo espressamente stabilito che non lo sappiamo dal fatto che stiamo cercando di gestire in genere eccezioni non rilevate.
Iron Gremlin,

3

Il problema con "eccezioni eccezionali", vale a dire eccezioni che non hai previsto, è che non sai in quale stato si trova il programma. Ad esempio, il tentativo di salvare i dati dell'utente potrebbe effettivamente distruggere ancora più dati .

Per tale motivo, è necessario chiudere l'applicazione.

C'è un'idea molto interessante chiamata Crash-only Software di George Candea e Armando Fox . L'idea è che se si progetta il proprio software in modo tale che l'unico modo per chiuderlo sia quello di bloccarlo e l'unico modo per avviarlo è il ripristino da un arresto anomalo, il software sarà più resiliente e il recupero degli errori i percorsi del codice saranno testati ed esercitati in modo molto più approfondito.

Hanno avuto questa idea dopo aver notato che alcuni sistemi si sono avviati più rapidamente dopo un arresto che dopo un arresto ordinato.

Un buon esempio, anche se non più rilevante, sono alcune versioni precedenti di Firefox che non solo si avviano più velocemente quando si ripristinano da un arresto anomalo, ma hanno anche un'esperienza di avvio migliore in questo modo ! In quelle versioni, se si spegne normalmente Firefox, si chiudono tutte le schede aperte e si avvia con una singola scheda vuota. Considerando che quando si ripristina da un crash, ripristina le schede aperte al momento del crash. (E quello era l'unico modo per chiudere Firefox senza perdere il tuo attuale contesto di navigazione.) Allora, cosa facevano le persone? Semplicemente non hanno mai chiuso Firefox e invece l'hanno sempre modificato pkill -KILL firefox.

C'è un bel commento sul software solo crash di Valerie Aurora su Linux Weekly News . Anche i commenti meritano una lettura. Ad esempio, qualcuno nei commenti sottolinea giustamente che quelle idee non sono nuove e sono in effetti più o meno equivalenti ai principi di progettazione delle applicazioni basate su Erlang / OTP. E, naturalmente, guardando questo oggi, altri 10 anni dopo quello di Valerie e 15 anni dopo l'articolo originale, potremmo notare che l'attuale campagna pubblicitaria di micro-servizi sta reinventando ancora quelle stesse idee. Il moderno design del data center su scala cloud è anche un esempio di una granularità più grossolana. (Qualsiasi computer può bloccarsi in qualsiasi momento senza influire sul sistema.)

Tuttavia, non è sufficiente lasciare solo il crash del software. Deve essere progettato per questo. Idealmente, il tuo software verrebbe suddiviso in piccoli componenti indipendenti che possono bloccarsi in modo indipendente. Inoltre, il "meccanismo di arresto anomalo" dovrebbe trovarsi al di fuori del componente in crash.


1

Il modo corretto di gestire la maggior parte delle eccezioni dovrebbe essere invalidare qualsiasi oggetto che potrebbe essere in uno stato corrotto di conseguenza e continuare l'esecuzione se gli oggetti invalidati non lo impediscono. Ad esempio, il paradigma sicuro per l'aggiornamento di una risorsa sarebbe:

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

Se si verifica un'eccezione imprevista durante l'aggiornamento della risorsa protetta, la risorsa deve essere presunta in uno stato corrotto e il blocco deve essere invalidato, indipendentemente dal fatto che l'eccezione sia di un tipo che sarebbe altrimenti benigno.

Sfortunatamente, le protezioni delle risorse implementate tramite IDisposable/ usingverranno rilasciate ogni volta che il blocco custodito esce, senza alcun modo di sapere se il blocco è uscito normalmente o in modo anomalo. Pertanto, anche se ci dovrebbero essere criteri ben definiti per quando continuare dopo un'eccezione, non c'è modo di dire quando si applicano.


+1 semplicemente per esprimere questa prospettiva relativamente non ovvia e ancora non comune su quale sia la strada giusta. In realtà non so ancora se sono d'accordo, perché questa è una nuova regola / euristica per me, quindi devo pensarci un po ', ma sembra plausibilmente saggia.
mtraceur,

0

È possibile utilizzare l'approccio seguito da ogni singola app iOS e MacOS: un'eccezione non rilevata rimuove immediatamente l'applicazione. Inoltre molti errori, come array fuori limite o semplicemente overflow aritmetico nelle applicazioni più recenti, fanno lo stesso. Nessun avvertimento

Nella mia esperienza molti utenti non se ne accorgono ma toccano di nuovo l'icona dell'app.

Ovviamente è necessario assicurarsi che un tale arresto non comporti una significativa perdita di dati e sicuramente non comporti errori costosi. Ma un avviso "La tua app andrà in crash adesso. Chiama l'assistenza se ti dà fastidio "non aiuta nessuno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.