トップ回答者
ThreadPool内での例外を外部でcatchしてアプリケーションが落ちないようにしたい

質問
-
WPFでThreadPool内で発生した例外が内部で捕捉されない場合、どこにも例外が補足されずアプリケーションが落ちてしまいます。
内部でcatchするのが一番ですが、ThreadPool内で発生した例外を外部でキャッチしてアプリケーションを落とさないようにする方法を知りたいと思っております。
catchできなくとも落ちない方法だけでもかまいません。
以下はThreadPoolで発生させた例外で落ちるサンプルコードになります。public MainWindow() { InitializeComponent(); //補足されない例外が発生しているかの確認 AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; try { //ThreadPoolで実行 ThreadPool.QueueUserWorkItem( new WaitCallback(this.foobar)); } catch (Exception e) { //ここには来ない。 } } private void foobar(object state) { throw new Exception(); } private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Console.WriteLine(e); }
UnhandledExcetionの内容:
例外がスローされました: 'System.Exception' (Sample.exe の中)
System.UnhandledExceptionEventArgs
型 'System.Exception' のハンドルされていない例外が 設計用プロジェクト.exe で発生しましたハンドルされていない例外: System.Exception: 種類 'System.Exception' の例外がスローされました。
場所 xxxx.MainWindow.ParseResponses(Object state) 場所 C:\xxxx\MainWindow.xaml.cs:行 61
場所 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
場所 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
場・・System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
場所 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
場所 System.Threading.ThreadPoolWorkQueue.Dispatch()
場所 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
回答
-
ThreadPool.QueueUserWorkItemではなくTaskを使えばいいです。
Taskを作るときに TaskCreationOptions.LongRunningを指定しなければ TheadPoolを使って実行されます。Task task = Task.Run(() => this.foobar(null)) .ContinueWith((t) => { //ContinueWithでつなげば、エラーが無かったか確認できる //必要ならここで外部にエラーを通知すればいいです if (t.Exception is System.AggregateException) { var aex=(System.AggregateException) t.Exception; System.Diagnostics.Debug.WriteLine(aex.InnerException.StackTrace); } else if (t.Exception != null) { System.Diagnostics.Debug.WriteLine(t.Exception.StackTrace); } });
それとも、try~catchの形式でThreadPool内のエラーをキャッチしたいのですか?
ならばasync/awaitでTaskを使えばできます。private async Task Test() { try { await Task.Run(() => this.foobar(null)); } catch(Exception e) { } }
どうしてもThreadPool.QueueUserWorkItemでやりたいなら、きちんと処理内でキャッチしましょう。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
-
結局のところ各スレッドにおいてコールスタックの最初まで例外がcatchされなければアプリケーションは終了されるという話で、Taskは大雑把には次のような処理をしているだけのことです。
ThreadPool.QueueUserWorkItem(() => { try { action(); } catch (Exception ex) { this.Exception = ex; } });
なので、必ずしもTaskじゃなくてもかまいません。まあTask以外を使う理由も特にない場合がほとんどでしょうけども。- 回答としてマーク yj0529 2019年11月1日 1:39
-
そもそも論として、例外を発生させるべきではありません。発生することが分かっている場合に、catchするのではなく、例外が発生しないようコードを修正するべきです。例外の推奨事項も確認してください。
で、そうであるならば、例外は開発者の意図しない状態ですので、未処理の場合にプロセスを終了させる、というのが.NETの設計方針のようです。
一方、TaskはthreadPoolとは意味合いが違います。Taskはデータありきの概念です。何等かの結果とそれを得るための処理という括りです。ですので、データを得る際に発生した例外はハンドリングされる、という設計です。
- 回答としてマーク yj0529 2019年11月1日 5:21
すべての返信
-
ThreadPool.QueueUserWorkItemではなくTaskを使えばいいです。
Taskを作るときに TaskCreationOptions.LongRunningを指定しなければ TheadPoolを使って実行されます。Task task = Task.Run(() => this.foobar(null)) .ContinueWith((t) => { //ContinueWithでつなげば、エラーが無かったか確認できる //必要ならここで外部にエラーを通知すればいいです if (t.Exception is System.AggregateException) { var aex=(System.AggregateException) t.Exception; System.Diagnostics.Debug.WriteLine(aex.InnerException.StackTrace); } else if (t.Exception != null) { System.Diagnostics.Debug.WriteLine(t.Exception.StackTrace); } });
それとも、try~catchの形式でThreadPool内のエラーをキャッチしたいのですか?
ならばasync/awaitでTaskを使えばできます。private async Task Test() { try { await Task.Run(() => this.foobar(null)); } catch(Exception e) { } }
どうしてもThreadPool.QueueUserWorkItemでやりたいなら、きちんと処理内でキャッチしましょう。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
-
結局のところ各スレッドにおいてコールスタックの最初まで例外がcatchされなければアプリケーションは終了されるという話で、Taskは大雑把には次のような処理をしているだけのことです。
ThreadPool.QueueUserWorkItem(() => { try { action(); } catch (Exception ex) { this.Exception = ex; } });
なので、必ずしもTaskじゃなくてもかまいません。まあTask以外を使う理由も特にない場合がほとんどでしょうけども。- 回答としてマーク yj0529 2019年11月1日 1:39
-
そもそも論として、例外を発生させるべきではありません。発生することが分かっている場合に、catchするのではなく、例外が発生しないようコードを修正するべきです。例外の推奨事項も確認してください。
で、そうであるならば、例外は開発者の意図しない状態ですので、未処理の場合にプロセスを終了させる、というのが.NETの設計方針のようです。
一方、TaskはthreadPoolとは意味合いが違います。Taskはデータありきの概念です。何等かの結果とそれを得るための処理という括りです。ですので、データを得る際に発生した例外はハンドリングされる、という設計です。
- 回答としてマーク yj0529 2019年11月1日 5:21