未処理の例外を処理する方法は?(アプリケーションを終了するか、生き続けるか)


30

デスクトップアプリケーションで未処理の例外が発生した場合のベストプラクティスは何ですか?

ユーザーがサポートに連絡できるように、ユーザーにメッセージを表示しようと考えていました。ユーザーにアプリケーションを再起動することをお勧めしますが、強制することはしません。ここで説明しているものと同様:ux.stackexchange.com-予期しないアプリケーションエラーを処理する最良の方法は何ですか?

プロジェクトは.NET WPFアプリケーションであるため、説明されている提案は次のようになります(これは簡略化された例です。ユーザーが[詳細の表示]をクリックして、エラーを簡単に報告してください):

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...
    }
}

実装(ViewModelsのコマンドまたは外部イベントのイベントハンドラー)では、特定の外因性例外のみをキャッチし、他のすべての例外(ボーンヘッドおよび未知の例外)を上記の「最後のリゾートハンドラー」までバブルさせます。骨抜きと外因性の例外の定義については、次を参照してください:Eric Lippert-Vexing exceptions

アプリケーションを終了するかどうかをユーザーに決定させるのは理にかなっていますか? アプリケーションが終了すると、一貫性のない状態になります...一方、ユーザーは、保存されていないデータを失ったり、アプリケーションが再起動されるまで、開始された外部プロセスを停止できなくなります。

または、作成しているアプリケーションのタイプに応じて、未処理の例外でアプリケーションを終了する必要があるかどうかは決定されますか?コードの完全版、第2版で説明されているように、「堅牢性」と「正確性」のトレードオフにすぎませんか

私たちが話しているアプリケーションの種類を説明するために、アプリケーションは主に化学実験装置を制御し、測定結果をユーザーに表示するために使用されます。そのために、WPFアプリケーションはいくつかのサービス(ローカルおよびリモートサービス)と通信します。WPFアプリケーションは、機器と直接通信しません。


27
例外を予期していなかった場合、アプリケーションが安全にプロッディングを継続できることをどのように確認できますか?
デデュプリケーター

2
@Deduplicator:もちろん、確実ではありません。マシューの答えに対するコメントとしてすでに書かれているように:「もちろん、アプリケーションは無効な状態になる可能性があります。一部のViewModelが部分的にしか更新されない可能性があります。サービスに送信すると、サービスはとにかくそれを受け入れません。ユーザーがアプリケーションを再起動する前に保存できる場合は、ユーザーにとって良いと思いませんか?」
ジョナスベンツ

2
@Vooでは、例外を常に期待することで、アプリケーションが安全に処理を続行できることを確認しますか?予期しない例外を受け取るという前提を否定しているようです。
デデュプリケーター

2
いずれにしても、エラーメッセージをコピー可能にしてください。または、どのログファイルに書き込まれたかを伝えます。
ComFreek

2
処理は必ずしも明示的なアクションを意味するわけではありません。アプリケーションが安全に続行できると確信できる場合は、例外を処理しました。
チェプナー

回答:


47

とにかく、電源障害やシステム全体をクラッシュさせる別のバックグラウンドプロセスなど、未処理の例外以外の理由でプログラムが終了することを期待する必要があります。そのため、アプリケーションを終了して再起動することをお勧めしますが、そのような再起動の結果緩和し、データ損失の可能性最小限に抑えるためのいくつかの対策を講じることをお勧めします。

以下のポイントの分析から始めます。

  • プログラムが終了した場合、実際にどれだけのデータが失われる可能性がありますか?

  • ユーザーにとってこのような損失はどれほど深刻ですか?失われたデータを5分未満で再構築できますか、それとも1日分の作業を失うことについて話しているのですか

  • 「中間バックアップ」戦略を実装するのにどれだけの労力がかかりますか?コメントで書いたように、通常の保存操作では「ユーザーが変更理由を入力する必要があるためこれを除外しないでください。プログラムが自動的にクラッシュした後にリロードされる可能性がある一時ファイルまたは状態のようなものを考えてください。多くの種類の生産性ソフトウェアがこれを行います(たとえば、MS OfficeとLibreOfficeの両方に「自動保存」機能とクラッシュリカバリがあります)。

  • データが間違っているか破損している場合、ユーザーはこれを簡単に見ることができますか(プログラムの再起動後)。はいの場合、ユーザーにデータを保存して(破損する可能性はわずかですが)、再起動を強制し、データをリロードして、データが正常かどうかをユーザーに確認させるオプションを提供できます。古いバージョンが破損しないように、定期的に保存された最後のバージョンを上書きしないようにしてください(代わりに一時的な場所/ファイルに書き込みます)。

このような「中間バックアップ」戦略が賢明なオプションである場合、最終的にはアプリケーションとそのアーキテクチャ、および関連するデータの性質と構造に依存します。しかし、ユーザーが10分未満の作業を失い、そのようなクラッシュが1週間に1回、またはめったに起こらない場合は、おそらくこれにあまり投資しません。


10
en.wikipedia.org/wiki/Crash-only_software、これがAndroidアプリが必要に応じて動作する方法です。
Mooing Duck

3
優れた答え-そして、より広いコンテキストで物事を検討する良い例(この場合、「クラッシュの場合にデータ損失をどのように防ぐことができますか?」)はより良いソリューションにつながります。
sleske

1
古いデータを上書きしてはならないことに注意するために小さな編集を行いました-気にしないでください。
sleske

1
@MooingDuck多数のAndroidアプリ(ゲームなど)は、クラッシュすると状態を失います。
user253751

1
@immibis:はい、Androidには本当に低品質のアプリがたくさんあります。
Mooing Duck

30

開発しているアプリケーションにある程度依存しますが、一般的に、アプリケーションで未処理の例外が発生した場合は、終了する必要があると思います。

どうして?

アプリケーションの状態に自信が持てなくなるためです。

間違いなく、有用なメッセージをユーザーに提供しますが、最終的にはアプリケーションを終了する必要があります。

あなたのコンテキストを考えると、私は間違いなくアプリケーションを終了したいと思います。ラボで実行されているソフトウェアが破損した出力を生成することは望ましくありません。例外を処理することを考えていなかったため、例外がスローされた理由と何が起こっているのかわかりません。


最後の部分で、アプリケーションに関するコンテキスト情報を追加しようとしました。
ジョナスベンツ

10
@JonasBenz ユーザーがアプリケーションを再起動する前に保存できるといいのではないでしょうか? はい、しかし、ユーザーが保存するデータが有効であり、破損していないことをどのように確認しますか?この時点で、予期しない例外が発生し、その理由は本当にわかりません。ユーザーにとって迷惑ですが、最も安全な方法は、アプリケーションを終了することです。ユーザーの作業を節約することに懸念がある場合は、常に節約する戦略を採用します。繰り返しますが、それはすべてあなたが書いているアプリケーションに依存します。
マタイ

4
はい、ここでも同じように主張できます。[続行]ボタンの存在に同意しません。問題は、あなたが安全に続行できるかどうかをアプリケーション開発者が知らない場合、ユーザーがどのように知ることができるかということです。未処理の例外が発生した場合、予期しないエラーが発生していることを意味し、この時点で何が起こっているのか確実に言えません。ユーザーは作業を失うことを望まないため続行したいと思いますが、私はそれを理解していますが、アプリケーションがこのエラーのために悪い結果を生む可能性がある場合でも、ユーザーに継続させたいですか?
マタイ

3
@Matthew 「あなたが、あなたが安全に続けることができるかどうか、アプリケーション開発者が知らないなら、ユーザーはどうやって知ることができるか」、開発者はコードを書いたときを知りませんでした。ユーザーがこのような特定のバグに遭遇した場合、それは既知である可能性があります。そして、ユーザーは、ユーザーフォーラム、サポートチャネル、その他のあらゆるものから、または単にデータをテストして確認するだけで、それを見つけることができます...ユーザーは、「継続」が賢明かどうかを実際に知ることができます。
ハイド

1
@ JonasBenz、Windows 3.1では、プログラムが不正なメモリアクセスを実行したときに表示されるダイアログボックスには、プログラムを実行し続ける「無視」ボタンがありました。Windowsのすべての後続バージョンにはそのボタンがないことに注意してください。
マーク

12

これは化学実験室向けであり、アプリケーションが機器を直接制御するのではなく、他のサービスを介して制御することを考慮してください。

メッセージを表示した後、強制終了します。未処理の例外の後、アプリケーションは不明な状態になります。誤ったコマンドを送信する可能性があります。それは鼻の悪魔さえ呼び出すことができます。誤ったコマンドは、潜在的に高価な試薬を無駄や機器や人々に危険をもたらす可能性があります

しかし、あなたは他の何かをすることができます:再起動後に正常に回復します。あなたのアプリケーションは、クラッシュしたときにそれらのバックグラウンドサービスをそれ自体でダウンさせないと仮定します。その場合、それらから簡単に状態を回復できます。または、さらに状態がある場合は、保存することを検討してください。データの原子性と整合性を備えたストレージ(SQLiteかもしれません)。

編集:

コメントで述べたように、あなたがコントロールするプロセスは、ユーザーが反応する時間がないほど十分に速い変更を必要とするかもしれません。その場合、正常な状態の回復に加えて、アプリのサイレント再起動を検討する必要があります。


フォローアップコマンドを必要とする状態での終了は、今すぐ化学実験室でも危険です。
オレグV.ボルコフ

@ OlegV.Volkovだから、おそらく終了時に自分自身を再起動しますか?まともなコンピューターでGUIを起動するには、数百ミリ秒のオーダーが必要です。プロセスがより厳しいタイミングを必要とする場合、制御は非リアルタイムOSに実装されません。最終的なリスク評価を行うのはOPです。
Jan Dorniak

@ OlegV.Volkovは良い点ですので、答えに自分の意見を追加しました。
Jan Dorniak

8

プログラムのトップレベルでこの質問に一般的に答えようとするのは賢明なことではありません。

何らかの問題が発生し、アプリケーションのアーキテクチャのどの時点でもこのケースを考慮しなかった場合、アクションが安全であるかどうかについて一般化することはできません。

だから、いいえ、アプリケーションと開発者がそれが可能か賢明であるかを見つけるために必要なデューデリジェンスを実証していないので、ユーザーがアプリケーションが回復しようとするかどうかを選択できるようにすることは一般的に許容できる設計ではありません。

ただし、この種の障害回復を念頭に置いて設計されたロジックまたは動作の価値の高い部分がアプリケーションにあり、この場合はそれらを活用することが可能であれば、どうしてもそうする-その場合、リカバリを試行するかどうかを確認するようにユーザーにプロンプ​​トを表示したり、単に呼び出して終了したり、最初からやり直したりすることができます。

この種の回復は一般にすべての(またはほとんどの)プログラムに必要または推奨されるわけではありませんが、この程度の運用上の整合性が必要なプログラムで作業している場合、この種のユーザーにプロンプ​​トを表示するのは正気です。

特別な障害回復ロジックが必要な場合-いいえ、これを実行しないでください。あなたは文字通り何が起こるかわからない、もしあなたがそうすれば、あなたは例外をさらに見つけてそれを処理しただろう。


残念ながら、「特定の場所から受信したデータを使用してオブジェクトを構築する」などの多くのメソッドは、アクションを完了できなかったことを示す例外と、副作用がないことを示す例外と、より深刻なことを示す例外とを区別しません間違っています。リソースをロードする試みが何らかの理由で予期していなかったために失敗したという事実は、一般的にオブジェクトを構築できない場合に致命的なエラーを強制するものではありません。重要なのは副作用であり、残念ながら例外フレームワークが無視するものです。
スーパーキャット

@supercat-エラーを特定できれば、それを処理できます。識別できない場合は、アプリケーションの状態の整合性チェックを実行して問題の原因を探そうとするルーチンを作成しない限り、処理できません。エラーが「何であったのか」は関係ありませんが、キャッチされていない例外を一般的に処理しようとしているという事実によって、それがわからないことが明確に確立されています。
鉄グレムリン

3

「例外的な例外」、つまりあなたが予見していない例外の問題は、プログラムがどの状態にあるかわからないことです。たとえば、ユーザーのデータを保存しようとすると、実際にはさらに多くのデータ破壊されます

そのため、アプリケーションを終了する必要があります。

George CandeaとArmando FoxによるCrash-only Softwareという非常に興味深いアイデアがあります。アイデアは、それを閉じる唯一の方法がクラッシュすることであり、それを開始する唯一の方法がクラッシュから回復することであるようにソフトウェアを設計する場合、ソフトウェアはより回復力があり、エラー回復コードパスはより徹底的にテストされ、実行されます。

一部のシステムは、正常なシャットダウン後よりもクラッシュ後の速く起動することに気付いた後、彼らはこのアイデアを思いつきました。

関係のない例ですが、クラッシュから回復する際の起動が速くなるだけでなく、起動時の操作性向上する Firefoxの古いバージョンがあります。これらのバージョンでは、Firefoxを正常にシャットダウンすると、開いているタブがすべて閉じられ、1つの空のタブで起動します。一方、クラッシュから復旧する場合、クラッシュ時に開いていたタブを復元します。(そして、それが現在のブラウジングコンテキストを失うことなくFirefoxを閉じる唯一の方法でした。)それで、人々は何をしましたか?彼らはFirefoxを決して閉じず、常にpkill -KILL firefoxそれを編集しました。

Linux Weekly Newsには、Valerie Auroraによるクラッシュ専用ソフトウェアに関する素晴らしい記事があります。コメントも読む価値があります。たとえば、コメントの誰かが、これらのアイデアは新しいものではなく、実際には、Erlang / OTPベースのアプリケーションの設計原理とほぼ同等であると指摘しています。もちろん、ヴァレリーの10年後、元の記事の15年後の今日、これを見ると、現在のマイクロサービスの誇大広告が同じアイデアを再発明していることに気付くかもしれません。最新のクラウド規模のデータセンター設計も、より粗い粒度の例です。(どのコンピューターでも、システムに影響を与えることなくいつでもクラッシュする可能性があります。)

ただし、ソフトウェアをクラッシュさせるだけでは不十分です。それのために設計する必要があります。理想的には、ソフトウェアは小さな独立したコンポーネントに分割され、それぞれが独立してクラッシュする可能性があります。また、「クラッシュメカニズム」は、クラッシュするコンポーネントの外部にある必要があります。


1

ほとんどの例外を処理する適切な方法は、結果として破損状態にある可能性のあるオブジェクトを無効化し、無効化されたオブジェクトがそれを妨げない場合は実行を継続することです。たとえば、リソースを更新するための安全なパラダイムは次のとおりです。

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

保護されたリソースの更新中に予期しない例外が発生した場合、例外が良性であるかどうかに関係なく、リソースは破損状態であると推定され、ロックが無効になります。

残念ながら、IDisposable/ usingを介して実装されたリソースガードは、ブロックされたブロックが正常に終了したか異常に終了したかを知ることなく、保護されたブロックが終了するたびに解放されます。したがって、例外の後にいつ続行するかについて明確に定義された基準があるはずですが、それらがいつ適用されるかを伝える方法はありません。


+1は、適切な方法が何であるかについて、この比較的自明ではなく、まだ一般的ではない視点を単に表現するために。これは私にとって新しい発見的/ルールであるため、同意するかどうかはまだ実際には分かりません。
mtraceur

0

すべてのiOSおよびMacOSアプリが従うアプローチを使用できます。キャッチされない例外は、アプリケーションをすぐに停止します。さらに、範囲外の配列や、新しいアプリケーションの算術オーバーフローなど、多くのエラーでも同じことが行われます。警告なし。

私の経験では、多くのユーザーは通知を受け取らず、アプリのアイコンをもう一度タップするだけです。

明らかに、このようなクラッシュが重大なデータ損失を引き起こさず、間違いなくコストのかかる間違いを引き起こさないことを確認する必要があります。ただし、「アプリはすぐにクラッシュします。わからない場合はサポートに電話してください」と言っても、誰も助けにはなりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.