.NET 4.5への移行時の非同期例外処理

投稿では、.NET 4.5のコンテキストで.NET 4の非同期コードで例外を処理するときに生じる落とし穴を明らかにしようとします。



いくつかの条件下で、下の例のコードが未処理の例外なしで終了する理由を知りたい場合は、catにようこそ。



サーバーにインストールされている.NETバージョンに応じて、サンプルコードの動作を検討してください。



class Program { static void Main(string[] args) { Task.Factory.StartNew(() => { throw new Exception(); }); Thread.Sleep(1000); GC.Collect(); } }
      
      







.NET 4がサーバーにインストールされていて、.NET 4.5がインストールされていない場合、未処理の例外によりプロセスは終了します。 ガベージコレクション中に、誰も完了しないと思われるタスクから除外されます。 例が次のようになった場合:



 class Program { static void Main(string[] args) { Task.Factory.StartNew(() => { throw new Exception(); }); Thread.Sleep(1000); } }
      
      







その問題は気づきにくいでしょう。



このような例外を処理するために、TaskScheduler型にはTaskUnobservedExceptionイベントがあります。 このイベントにより、例外をキャッチしてプロセスの完了を防ぐことができます。



.NET 4.5がサーバーにインストールされている場合、動作が変更され、未処理の例外が原因でプロセスは終了しません。

.NET 4の標準的な動作が.NET 4.5に残っている場合、以下の例では、SomeMethodを呼び出した後にガベージを収集すると、プロセスが終了します Async2()からの例外は未処理のままになります。



 public static async Task SomeMethod() { try { var t1 = Async1(); var t2 = Async2(); await t1; await t2; } catch { } } public static Task Async1() { return Task.Factory.StartNew(() => { throw new Exception(); }); } public static Task Async2() { return Task.Factory.StartNew(() => { throw new Exception(); }); }
      
      







.NET 4.5のインストール後に.NET 4から標準の動作に戻すには、 ThrowUnobservedTaskExceptionsキーをアプリケーション構成ファイルに追加する必要があります。



実際には、フレームワークの1つのバージョンから別のバージョンに切り替えるときのこのような動作の変更は、.NET 4.5がライブにインストールされず、開発者が.NET 4.5のシステムで作業するため危険です。 この場合、開発者は同様のエラーを見逃す可能性があります。 したがって、開発中は、 ThrowUnobservedTaskExceptionsキーを有効にしてアプリケーションをテストすることを強くお勧めします。



.NET 4.5には、多くの問題を引き起こす可能性のある別のイノベーションがあります。非同期のvoidメソッドは、非同期のTaskメソッドとは異なる方法でコンパイラによって処理されます。 非同期voidメソッドの処理にはAsyncVoidMethodBuilderが使用され、非同期タスクメソッドにはAsyncTaskMethodBuilderが使用されます。 エラーが発生した場合、同期コンテキストがない場合は、例外がスレッドプールにスローされ、プロセスが完了します。 このような例外をキャッチできますが、プロセスの完了を防ぐことはできません。 async voidメソッドは、UI要素からのイベントを処理するためにのみ使用する必要があります。



プロセスの完了につながる非同期のvoidメソッドの非自明な使用の例:



 new List<int>().ForEach(async i => { throw new Exception(); });
      
      





非同期voidメソッドが必要ない場合は、CIにルールを追加して、ILにAsyncVoidMethodBuilderが表示されたときに、ビルドが失敗したと見なすことができます。



ソース:

  1. http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspx
  2. http://blogs.msdn.com/b/cellfish/archive/2012/10/18/the-tale-of-an-unobservedtaskexception.aspx
  3. http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/10217876.aspx



All Articles