Bagaimana cara memperlakukan pengecualian yang tidak ditangani? (Hentikan aplikasi vs. Tetap hidup)

30

Apa praktik terbaik ketika pengecualian yang tidak ditangani terjadi di aplikasi desktop?

Saya sedang berpikir untuk menunjukkan pesan kepada pengguna, sehingga ia dapat menghubungi dukungan. Saya akan merekomendasikan kepada pengguna untuk me-restart aplikasi, tetapi tidak memaksanya. Mirip dengan apa yang dibahas di sini: ux.stackexchange.com - Apa cara terbaik untuk menangani kesalahan aplikasi yang tidak terduga?

Proyek ini adalah aplikasi .NET WPF, sehingga proposal yang dideskripsikan dapat terlihat seperti ini (Perhatikan bahwa ini adalah contoh yang disederhanakan. Mungkin akan masuk akal untuk menyembunyikan detail pengecualian sampai pengguna mengklik "Tampilkan Detail" dan menyediakan beberapa fungsionalitas untuk mudah melaporkan kesalahan):

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

Dalam implementasinya (Perintah ViewModels atau event handler dari peristiwa eksternal) saya kemudian hanya akan menangkap pengecualian eksogen spesifik dan membiarkan semua pengecualian lain (pengecualian berkepala dan tidak dikenal) melambung hingga "Handler resor terakhir" yang dijelaskan di atas. Untuk definisi pengecualian yang dikepalai dan eksogen, lihat: Eric Lippert - pengecualian Vexing

Apakah masuk akal untuk membiarkan pengguna memutuskan apakah aplikasi harus dihentikan? Ketika aplikasi dihentikan, maka Anda pasti tidak memiliki keadaan tidak konsisten ... Di sisi lain pengguna dapat kehilangan data yang belum disimpan atau tidak dapat menghentikan proses eksternal yang dimulai lagi sampai aplikasi dimulai ulang.

Atau keputusan apakah Anda harus menghentikan aplikasi pada pengecualian tidak tertangani tergantung pada jenis aplikasi yang Anda tulis? Apakah itu hanya pertukaran antara "ketahanan" vs "kebenaran" seperti yang dijelaskan dalam Kode Lengkap, Edisi Kedua

Untuk memberi Anda beberapa konteks jenis aplikasi yang sedang kita bicarakan: Aplikasi ini terutama digunakan untuk mengontrol instrumen laboratorium kimia dan menunjukkan hasil yang diukur kepada pengguna. Untuk melakukannya, aplikasi WPF berkomunikasi dengan beberapa layanan (layanan lokal dan jarak jauh). Aplikasi WPF tidak berkomunikasi langsung dengan instrumen.

Jonas Benz
sumber
27
Jika Anda tidak mengharapkan pengecualian, bagaimana Anda bisa yakin bahwa aplikasi dapat tetap berjalan dengan aman?
Deduplicator
2
@Dupuplikator: Tentu saja, Anda tidak bisa memastikan. Seperti sudah dituliskan sebagai komentar terhadap jawaban Matthew : "Ya, tentu saja aplikasi bisa dalam keadaan tidak valid. Mungkin beberapa ViewModel hanya mendapat pembaruan sebagian. Tapi bisakah ini membahayakan? Pengguna dapat memuat ulang data dan jika sesuatu yang tidak valid akan menjadi kirim ke layanan, maka layanan tidak akan menerimanya. Bukankah lebih baik bagi pengguna jika ia dapat menyimpan sebelum ia memulai ulang aplikasi? "
Jonas Benz
2
@ Vooo Jadi, Anda memastikan aplikasi dapat tetap berjalan dengan aman dengan selalu mengharapkan pengecualian? Tampaknya Anda menyangkal premis menerima pengecualian yang tidak terduga.
Deduplicator
2
Dalam hal apa pun, harap membuat pesan kesalahan dapat disalin. Atau, katakan di mana file log itu telah ditulis.
ComFreek
2
Penanganan tidak selalu berarti tindakan eksplisit. Jika Anda dapat memastikan aplikasi dapat melanjutkan dengan aman, Anda telah menangani pengecualian.
chepner

Jawaban:

47

Anda harus mengharapkan program Anda berakhir karena lebih dari sekadar pengecualian yang tidak ditangani, seperti kegagalan daya, atau proses latar belakang berbeda yang membuat seluruh sistem lumpuh. Oleh karena itu saya akan merekomendasikan untuk menghentikan dan me-restart aplikasi, tetapi dengan beberapa langkah untuk mengurangi konsekuensi dari restart seperti itu dan meminimalkan kemungkinan kehilangan data .

Mulailah dengan menganalisis poin-poin berikut:

  • Berapa banyak data yang benar-benar dapat hilang jika terjadi penghentian program?

  • Seberapa parah kerugian seperti itu bagi pengguna? Dapatkah data yang hilang direkonstruksi dalam waktu kurang dari 5 menit, atau apakah kita berbicara tentang kehilangan satu hari kerja?

  • Berapa banyak upaya untuk menerapkan beberapa strategi "cadangan menengah"? Jangan mengesampingkan ini karena "pengguna harus memasukkan alasan perubahan" pada operasi penyimpanan reguler, seperti yang Anda tulis dalam komentar. Lebih baik pikirkan sesuatu seperti file atau status sementara, yang dapat dimuat ulang setelah program macet secara otomatis. Banyak jenis perangkat lunak produktivitas melakukan ini (misalnya MS Office dan LibreOffice keduanya memiliki fitur "penyimpanan otomatis" dan pemulihan kerusakan).

  • Jika data salah atau rusak, dapatkah pengguna melihat ini dengan mudah (mungkin setelah restart program)? Jika ya, Anda dapat menawarkan opsi untuk membiarkan pengguna menyimpan data (dengan kemungkinan kecil itu rusak), kemudian memaksa restart, muat ulang dan biarkan pengguna memeriksa apakah data terlihat baik-baik saja. Pastikan untuk tidak menimpa versi terakhir yang disimpan secara teratur (alih-alih menulis ke lokasi / file sementara) untuk menghindari kerusakan versi lama.

Jika strategi "intermediate backup" seperti itu merupakan opsi yang masuk akal pada akhirnya tergantung pada aplikasi dan arsitekturnya, dan pada sifat serta struktur data yang terlibat. Tetapi jika pengguna akan kehilangan kurang dari 10 menit kerja, dan crash seperti itu terjadi seminggu sekali atau bahkan lebih jarang, saya mungkin tidak akan berinvestasi terlalu banyak memikirkan hal ini.

Doc Brown
sumber
10
en.wikipedia.org/wiki/Crash-only_software , dan ini adalah bagaimana aplikasi Android bekerja berdasarkan kebutuhan.
Mooing Duck
3
Jawaban yang sangat baik - dan contoh yang bagus untuk mempertimbangkan hal-hal dalam konteks yang lebih luas (dalam hal ini "bagaimana kita bisa mencegah kehilangan data dalam setiap kasus kerusakan?") Mengarah ke solusi yang lebih baik.
sleske
1
Saya menyunting kecil untuk mencatat bahwa Anda tidak boleh menimpa data lama - harap Anda tidak keberatan.
sleske
1
@MooingDuck Banyak aplikasi Android (seperti game) kehilangan statusnya karena macet.
user253751
1
@immibis: Ya, Android memang memiliki banyak aplikasi berkualitas sangat rendah.
Mooing Duck
30

Tergantung pada batas tertentu pada aplikasi yang Anda kembangkan tetapi secara umum, saya akan mengatakan bahwa jika aplikasi Anda menghadapi pengecualian yang tidak tertangani, Anda harus menghentikannya.

Mengapa?

Karena Anda tidak dapat lagi memiliki kepercayaan pada keadaan aplikasi.

Jelas, berikan pesan yang bermanfaat bagi pengguna, tetapi pada akhirnya Anda harus menghentikan aplikasi.

Mengingat konteks Anda, saya pasti ingin aplikasi untuk dihentikan. Anda tidak ingin perangkat lunak yang berjalan di laboratorium menghasilkan output yang korup dan karena Anda tidak berpikir untuk menangani pengecualian, Anda tidak tahu mengapa itu dilemparkan dan apa yang terjadi.

Matius
sumber
Saya mencoba menambahkan beberapa informasi konteks tentang aplikasi di bagian terakhir.
Jonas Benz
10
@JonasBenz Bukankah lebih baik bagi pengguna jika ia dapat menyimpan sebelum memulai ulang aplikasi? Ya, tetapi bagaimana Anda tahu jika data yang disimpan oleh pengguna itu valid dan tidak rusak? Pada titik ini, Anda memiliki pengecualian yang tidak terduga dan Anda benar-benar tidak tahu mengapa. Jalur teraman Anda, meskipun menjengkelkan bagi pengguna, adalah menghentikan aplikasi. Jika Anda khawatir tentang pekerjaan penghematan pengguna, Anda akan menggunakan strategi penghematan konstan. Sekali lagi, semuanya tergantung pada aplikasi yang Anda tulis.
Matius
4
Ya, saya dapat berdebat dengan cara yang sama di sini: Saya tidak setuju dengan kehadiran tombol Lanjutkan. Masalahnya adalah bahwa jika Anda, pengembang aplikasi tidak tahu apakah Anda dapat melanjutkan dengan aman, bagaimana pengguna bisa tahu? Jika Anda mendapatkan pengecualian yang tidak ditangani, itu berarti Anda memiliki kesalahan yang tidak Anda harapkan dan Anda tidak bisa mengatakan dengan pasti apa yang terjadi pada titik ini. Pengguna akan ingin melanjutkan karena mereka tidak ingin kehilangan pekerjaan mereka, saya mengerti itu, tetapi apakah Anda ingin membiarkan mereka melanjutkan bahkan jika aplikasi Anda dapat menghasilkan hasil yang buruk karena kesalahan ini?
Matius
3
@ Matthew "jika Anda, pengembang aplikasi tidak tahu apakah Anda dapat melanjutkan dengan aman, bagaimana pengguna bisa tahu" , pengembang tidak tahu kapan mereka menulis kode. Ketika pengguna menemukan bug tertentu seperti ini, itu mungkin diketahui. Dan pengguna dapat mencari tahu dari forum pengguna apa pun, saluran dukungan dan yang lainnya, atau hanya dengan menguji dan melihat apa yang terjadi pada data mereka ... Saya setuju itu masih agak terlalu kabur dan berbahaya sebagai fitur pengguna, hanya menunjukkan bahwa waktu membuatnya mungkin bagi pengguna untuk mengetahui apakah "melanjutkan" masuk akal atau tidak.
Hyde
1
@JonasBenz, di bawah Windows 3.1, kotak dialog yang muncul ketika sebuah program melakukan akses memori ilegal memiliki tombol "abaikan" yang memungkinkan program terus berjalan. Anda akan perhatikan bahwa setiap versi Windows berikutnya tidak memiliki tombol itu.
Tandai
12

Menimbang bahwa ini dimaksudkan untuk laboratorium kimia dan aplikasi Anda tidak mengendalikan instrumen secara langsung melainkan melalui layanan lain:

Memaksa penghentian setelah menampilkan pesan. Setelah pengecualian yang tidak ditangani, aplikasi Anda berada dalam kondisi yang tidak diketahui. Itu bisa mengirim perintah yang salah. Ia bahkan bisa memanggil setan hidung . Perintah yang salah berpotensi membuang reagen yang mahal atau membahayakan peralatan atau manusia .

Tetapi Anda dapat melakukan sesuatu yang lain: memulihkan dengan anggun setelah memulai kembali . Saya berasumsi bahwa aplikasi Anda tidak menjatuhkan layanan latar belakang itu sendiri ketika crash. Dalam hal ini Anda dapat dengan mudah memulihkan keadaan dari mereka. Atau, jika Anda memiliki lebih banyak status, pertimbangkan untuk menyimpannya. Dalam penyimpanan yang memiliki ketentuan untuk atomicity dan integritas data (SQLite mungkin?).

Edit:

Seperti yang dinyatakan dalam komentar, proses yang Anda kontrol mungkin memerlukan perubahan cukup cepat sehingga pengguna tidak akan punya waktu untuk bereaksi. Dalam hal ini Anda harus mempertimbangkan untuk memulai kembali aplikasi secara diam-diam sebagai tambahan untuk pemulihan keadaan yang anggun.

Jan Dorniak
sumber
Pengakhiran dalam keadaan yang memerlukan perintah tindak lanjut SEKARANG JUGA bisa sama berbahayanya di laboratorium kimia.
Oleg V. Volkov
@ OlegV.Volkov jadi mungkin restart sendiri setelah penghentian? Pada komputer yang layak memulai GUI harus mengambil urutan ratusan milidetik atas. Jika proses ini membutuhkan waktu yang lebih sulit, kontrol tidak akan diterapkan pada OS non-waktu nyata. Meskipun itu OP yang harus melakukan penilaian risiko akhir.
Jan Dorniak
@ OlegV.Volkov itu poin yang bagus, jadi saya menambahkan pendapat saya tentang itu dalam jawabannya.
Jan Dorniak
8

Mencoba menjawab pertanyaan ini secara umum di level teratas program bukan permainan yang cerdas.

Jika ada sesuatu yang menggelegak sepanjang jalan, dan tidak ada dalam arsitektur aplikasi apakah ada yang mempertimbangkan kasus ini, Anda tidak memiliki generalisasi yang dapat Anda buat tentang tindakan apa, atau tidak, aman untuk diambil.

Jadi, tidak, itu jelas bukan desain yang dapat diterima secara umum untuk memungkinkan pengguna untuk memilih apakah aplikasi akan pulih atau tidak, karena aplikasi dan pengembang belum melakukan uji tuntas yang diperlukan untuk mencari tahu apakah itu mungkin atau bahkan bijaksana. .

Namun, jika aplikasi memiliki bagian bernilai tinggi dari logika atau perilaku yang telah direkayasa dengan pemulihan kegagalan semacam ini dalam pikiran, dan dimungkinkan untuk memanfaatkannya dalam hal ini, maka dengan segala cara, lakukanlah - Jika demikian , mungkin dapat diterima untuk meminta pengguna untuk melihat apakah mereka ingin mencoba pemulihan, atau jika mereka hanya ingin menyebutnya berhenti dan mulai lagi dari awal.

Pemulihan semacam ini umumnya tidak diperlukan atau disarankan untuk semua (atau bahkan sebagian besar) program, tetapi, jika Anda bekerja pada suatu program yang memerlukan tingkat integritas operasional seperti ini, itu mungkin merupakan keadaan di mana penyajian semacam ini meminta pengguna akan menjadi hal yang wajar untuk dilakukan.

Dalam leiu dari logika pemulihan kegagalan khusus - Tidak, jangan lakukan ini. Anda benar-benar tidak tahu apa yang akan terjadi, jika Anda melakukannya, Anda akan menangkap pengecualian lebih jauh dan menanganinya.

Besi GREMLIN
sumber
Sayangnya, banyak metode untuk hal-hal seperti "Membangun objek dengan data yang diterima dari beberapa lokasi" tidak membuat perbedaan antara pengecualian yang menunjukkan bahwa tindakan tidak dapat diselesaikan, tetapi upaya tersebut tidak memiliki efek samping, dibandingkan dengan yang menunjukkan bahwa sesuatu yang lebih serius salah. Fakta bahwa upaya memuat sumber daya gagal karena suatu alasan yang tidak diantisipasi seharusnya tidak memaksakan kesalahan fatal jika seseorang pada umumnya dipersiapkan untuk ketidakmampuan membangun objek. Yang penting adalah efek samping, yang sayangnya diabaikan kerangka kerja pengecualian.
supercat
@supercat - Jika Anda dapat mengidentifikasi kesalahan, Anda bisa mengatasinya. Jika Anda tidak dapat mengidentifikasi itu, Anda tidak bisa mengatasinya, kecuali jika Anda menulis rutin untuk menjalankan pemeriksaan integritas pada keadaan aplikasi untuk mencoba membersihkan apa yang mungkin salah. Tidak masalah apa kesalahannya ',' kami telah menyatakan dengan tegas bahwa kami tidak mengetahui hal itu dengan fakta bahwa kami berusaha untuk secara umum menangani pengecualian yang tidak tertangkap.
Iron GREMLIN
3

Masalah dengan "pengecualian luar biasa", yaitu pengecualian yang belum Anda perkirakan, adalah Anda tidak tahu status program itu. Misalnya, mencoba menyimpan data pengguna sebenarnya dapat menghancurkan lebih banyak data .

Karena itu, Anda harus menghentikan aplikasi.

Ada ide yang sangat menarik yang disebut Crash-only Software oleh George Candea dan Armando Fox . Idenya adalah bahwa jika Anda merancang perangkat lunak Anda sedemikian rupa sehingga satu-satunya cara untuk menutupnya adalah dengan menghancurkannya dan satu-satunya cara untuk memulainya adalah dengan memulihkan dari kerusakan, maka perangkat lunak Anda akan lebih tangguh, dan pemulihan kesalahan jalur kode akan jauh lebih teruji dan dilaksanakan.

Mereka datang dengan ide ini setelah memperhatikan bahwa beberapa sistem mulai lebih cepat setelah crash daripada setelah shutdown tertib.

Contoh yang bagus, meskipun tidak lagi relevan, adalah beberapa versi Firefox yang lebih lama yang tidak hanya mulai lebih cepat saat memulihkan dari kerusakan, tetapi juga memiliki pengalaman startup yang lebih baik seperti itu ! Dalam versi itu, jika Anda mematikan Firefox secara normal, itu akan menutup semua tab yang terbuka dan memulai dengan satu tab kosong. Sedangkan ketika memulihkan dari kerusakan, itu akan mengembalikan tab yang terbuka pada saat kecelakaan. (Dan itulah satu-satunya cara untuk menutup Firefox tanpa kehilangan konteks penelusuran Anda saat ini.) Jadi, apa yang dilakukan orang? Mereka tidak pernah menutup Firefox dan malah selalu pkill -KILL firefoxmengubahnya.

Ada artikel bagus tentang perangkat lunak hanya-crash oleh Valerie Aurora di Linux Weekly News . Komentar juga layak dibaca. Sebagai contoh, seseorang dalam komentar dengan tepat menunjukkan bahwa ide-ide itu tidak baru, dan pada kenyataannya lebih atau kurang setara dengan prinsip-prinsip desain aplikasi berbasis Erlang / OTP. Dan, tentu saja, melihat ini hari ini, 10 tahun setelah Valerie dan 15 tahun setelah artikel asli, kita mungkin memperhatikan bahwa sensasi layanan mikro saat ini adalah menciptakan kembali ide-ide yang sama lagi. Desain pusat data skala Cloud modern juga merupakan contoh pada granularity yang lebih kasar. (Komputer apa pun dapat macet kapan saja tanpa mempengaruhi sistem.)

Namun, tidak cukup hanya membiarkan perangkat lunak Anda rusak. Itu harus dirancang untuk itu. Idealnya, perangkat lunak Anda akan dipecah menjadi komponen kecil dan independen yang masing-masing dapat macet secara independen. Juga, "mekanisme kerusakan" harus di luar komponen yang sedang macet.

Jörg W Mittag
sumber
1

Cara yang tepat untuk menangani sebagian besar pengecualian adalah dengan membatalkan objek apa pun yang mungkin berada dalam kondisi korup, dan melanjutkan eksekusi jika objek yang tidak valid tidak mencegahnya. Misalnya, paradigma aman untuk memperbarui sumber daya adalah:

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

Jika pengecualian yang tidak terduga terjadi saat memperbarui sumber daya yang dijaga, sumber daya tersebut harus dianggap dalam keadaan korup, dan kunci tidak valid, terlepas dari apakah pengecualian adalah tipe yang seharusnya tidak berbahaya.

Sayangnya, penjaga sumber daya diimplementasikan melalui IDisposable/ usingakan dilepaskan setiap kali blok yang dijaga keluar, tanpa ada cara untuk mengetahui apakah blok keluar secara normal atau tidak normal. Jadi, meskipun harus ada kriteria yang jelas untuk kapan harus melanjutkan setelah pengecualian, tidak ada cara untuk mengatakan kapan mereka berlaku.

supercat
sumber
+1 hanya untuk mengekspresikan perspektif yang relatif tidak jelas dan masih tidak umum ini tentang apa cara yang tepat. Saya belum benar-benar tahu apakah saya setuju dengan itu, karena ini adalah heuristik / aturan baru bagi saya, jadi saya harus merenungkannya untuk sementara waktu, tetapi tampaknya masuk akal.
mtraceur
0

Anda dapat menggunakan pendekatan yang diikuti oleh setiap aplikasi iOS dan MacOS: Pengecualian yang tidak dilakukan segera menghapus aplikasi. Ditambah banyak kesalahan, seperti array di luar batas atau hanya aritmatika overflow dalam aplikasi yang lebih baru melakukan hal yang sama. Tanpa peringatan.

Dalam pengalaman saya, banyak pengguna yang tidak memperhatikan tetapi cukup ketuk ikon aplikasi lagi.

Jelas Anda perlu memastikan bahwa kerusakan seperti itu tidak menyebabkan kehilangan data yang signifikan dan jelas tidak menyebabkan kesalahan yang mahal. Tetapi peringatan “Aplikasi Anda akan macet sekarang. Hubungi dukungan jika itu mengganggu Anda ”tidak membantu siapa pun.

gnasher729
sumber