例外処理 (タスク並列ライブラリ)Exception handling (Task Parallel Library)

タスク内で実行中のユーザー コードによってスローされた、ハンドルされない例外は、呼び出し元のスレッドに反映されます。ただし、このトピックの後半で説明している特定の状況を除きます。Unhandled exceptions that are thrown by user code that is running inside a task are propagated back to the calling thread, except in certain scenarios that are described later in this topic. 静的な、またはインスタンスの Task.Wait メソッドの 1 つを使用し、その呼び出しを try/catch ステートメント内に入れて例外を処理すると、例外が反映されます。Exceptions are propagated when you use one of the static or instance Task.Wait methods, and you handle them by enclosing the call in a try/catch statement. タスクが、アタッチされた子タスクの親である場合、または複数のタスクを待機している場合、複数の例外がスローされることがあります。If a task is the parent of attached child tasks, or if you are waiting on multiple tasks, multiple exceptions could be thrown.

呼び出し元のスレッドにすべての例外を反映するために、Task インフラストラクチャが例外を AggregateException インスタンスにラップします。To propagate all the exceptions back to the calling thread, the Task infrastructure wraps them in an AggregateException instance. AggregateException 例外には、スローされた元のすべての例外を調べるために列挙できる InnerExceptions プロパティがあり、個々に処理したり未処理にしたりできます。The AggregateException exception has an InnerExceptions property that can be enumerated to examine all the original exceptions that were thrown, and handle (or not handle) each one individually. また、 AggregateException.Handle メソッドを使用して元の例外を処理することもできます。You can also handle the original exceptions by using the AggregateException.Handle method.

例外が 1 つだけスローされた場合でも、次の例のように AggregateException 例外にラップされます。Even if only one exception is thrown, it is still wrapped in an AggregateException exception, as the following example shows.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var task1 = Task.Run( () => { throw new CustomException("This exception is expected!"); } );

      try
      {
          task1.Wait();
      }
      catch (AggregateException ae)
      {
          foreach (var e in ae.InnerExceptions) {
              // Handle the custom exception.
              if (e is CustomException) {
                  Console.WriteLine(e.Message);
              }
              // Rethrow any other exception.
              else {
                  throw;
              }
          }
      }
   }
}

public class CustomException : Exception
{
   public CustomException(String message) : base(message)
   {}
}
// The example displays the following output:
//        This exception is expected!
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim task1 = Task.Run(Sub() Throw New CustomException("This exception is expected!"))

      Try
         task1.Wait()
      Catch ae As AggregateException
         For Each ex In ae.InnerExceptions
            ' Handle the custom exception.
            If TypeOf ex Is CustomException Then
               Console.WriteLine(ex.Message)
            ' Rethrow any other exception.
            Else
               Throw
            End If
         Next
      End Try
   End Sub
End Module

Class CustomException : Inherits Exception
   Public Sub New(s As String)
      MyBase.New(s)
   End Sub
End Class
' The example displays the following output:
'       This exception is expected!

ハンドルされない例外は、 AggregateException をキャッチして、内部例外を確認しないことで回避できます。You could avoid an unhandled exception by just catching the AggregateException and not observing any of the inner exceptions. ただし、並列でない状況で基本的な Exception の種類をキャッチする場合と類似しているため、この操作はお勧めしません。However, we recommend that you do not do this because it is analogous to catching the base Exception type in non-parallel scenarios. 特定の操作を取得することなく例外をキャッチして回復しようとすると、プログラムが中間状態のままになるおそれがあります。To catch an exception without taking specific actions to recover from it can leave your program in an indeterminate state.

Task.Wait メソッドを呼び出してタスクの完了を待機する処理を行わない場合は、次の例のようにタスクの Exception プロパティから AggregateException 例外を取得することもできます。If you do not want to call the Task.Wait method to wait for a task's completion, you can also retrieve the AggregateException exception from the task's Exception property, as the following example shows. 詳細については、このトピックの「Task.Exception プロパティによる例外の確認」セクションを参照してください。For more information, see the Observing exceptions by using the Task.Exception property section in this topic.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var task1 = Task.Run( () => { throw new CustomException("This exception is expected!"); } );

      while(! task1.IsCompleted) {}

      if (task1.Status == TaskStatus.Faulted) {
          foreach (var e in task1.Exception.InnerExceptions) {
              // Handle the custom exception.
              if (e is CustomException) {
                  Console.WriteLine(e.Message);
              }
              // Rethrow any other exception.
              else {
                  throw e;
              }
          }
      }
   }
}

public class CustomException : Exception
{
   public CustomException(String message) : base(message)
   {}
}
// The example displays the following output:
//        This exception is expected!
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim task1 = Task.Run(Sub() Throw New CustomException("This exception is expected!"))

      While Not task1.IsCompleted
      End While

      If task1.Status = TaskStatus.Faulted Then
         For Each ex In task1.Exception.InnerExceptions
            ' Handle the custom exception.
            If TypeOf ex Is CustomException Then
               Console.WriteLine(ex.Message)
            ' Rethrow any other exception.
            Else
               Throw ex
            End If
         Next
      End If
   End Sub
End Module

Class CustomException : Inherits Exception
   Public Sub New(s As String)
      MyBase.New(s)
   End Sub
End Class
' The example displays the following output:
'       This exception is expected!

例外を反映するタスクを待機しない場合、またはタスクの Exception プロパティにアクセスする場合、例外はタスクがガベージ コレクトされるときに .NET の例外ポリシーに従ってエスカレートされます。If you do not wait on a task that propagates an exception, or access its Exception property, the exception is escalated according to the .NET exception policy when the task is garbage-collected.

連結しているスレッドへ例外が上方向に通知されると、例外が発生した後も、タスクによって一部の項目の処理が続行される可能性があります。When exceptions are allowed to bubble up back to the joining thread, it is possible that a task may continue to process some items after the exception is raised.

注意

[マイ コードのみ] が有効になっている場合、Visual Studio では、例外をスローする行で処理が中断され、"ユーザー コードで処理されない例外" に関するエラー メッセージが表示されることがあります。When "Just My Code" is enabled, Visual Studio in some cases will break on the line that throws the exception and display an error message that says "exception not handled by user code." このエラーは問題にはなりません。This error is benign. F5 キーを押して続行し、以下の例に示す例外処理動作を確認できます。You can press F5 to continue and see the exception-handling behavior that is demonstrated in these examples. Visual Studio による処理が最初のエラーで中断しないようにするには、 [ツール] メニューの [オプション]、[デバッグ] の順にクリックし、[全般] で [マイ コードのみを有効にする] チェック ボックスをオフにします。To prevent Visual Studio from breaking on the first error, just uncheck the Enable Just My Code checkbox under Tools, Options, Debugging, General.

アタッチされた子タスクと入れ子の AggregateExceptionsAttached child tasks and nested AggregateExceptions

タスクに、例外をスローする子タスクがアタッチされている場合、その例外は親タスクに反映される前に AggregateException でラップされます。つまり、呼び出し元のスレッドに反映される前に、その例外が固有の AggregateException でラップされるということです。If a task has an attached child task that throws an exception, that exception is wrapped in an AggregateException before it is propagated to the parent task, which wraps that exception in its own AggregateException before it propagates it back to the calling thread. このような場合、Task.WaitWaitAnyWaitAll の各メソッドでキャッチされた AggregateException 例外の InnerExceptions プロパティには、違反の原因となった元の例外ではなく、1 つ以上の AggregateException インスタンスが含まれます。In such cases, the InnerExceptions property of the AggregateException exception that is caught at the Task.Wait, WaitAny, or WaitAll method contains one or more AggregateException instances, not the original exceptions that caused the fault. 入れ子の AggregateException 例外を反復処理しなくて済むようにするには、Flatten メソッドを使用して入れ子の AggregateException をすべて削除します。これにより、AggregateException.InnerExceptions プロパティに元の例外が含まれるようになります。To avoid having to iterate over nested AggregateException exceptions, you can use the Flatten method to remove all the nested AggregateException exceptions, so that the AggregateException.InnerExceptions property contains the original exceptions. 次の例では、入れ子の AggregateException インスタンスが 1 つのループで平坦化され、処理されています。In the following example, nested AggregateException instances are flattened and handled in just one loop.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var task1 = Task.Factory.StartNew(() => {
                     var child1 = Task.Factory.StartNew(() => {
                        var child2 = Task.Factory.StartNew(() => {
                            // This exception is nested inside three AggregateExceptions.
                            throw new CustomException("Attached child2 faulted.");
                        }, TaskCreationOptions.AttachedToParent);

                        // This exception is nested inside two AggregateExceptions.
                        throw new CustomException("Attached child1 faulted.");
                     }, TaskCreationOptions.AttachedToParent);
      });

      try {
         task1.Wait();
      }
      catch (AggregateException ae) {
         foreach (var e in ae.Flatten().InnerExceptions) {
            if (e is CustomException) {
               Console.WriteLine(e.Message);
            }
            else {
               throw;
            }
         }
      }
   }
}

public class CustomException : Exception
{
   public CustomException(String message) : base(message)
   {}
}
// The example displays the following output:
//    Attached child1 faulted.
//    Attached child2 faulted.
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim task1 = Task.Factory.StartNew(Sub()
                                           Dim child1 = Task.Factory.StartNew(Sub()
                                                                                 Dim child2 = Task.Factory.StartNew(Sub()
                                                                                                                       Throw New CustomException("Attached child2 faulted.")
                                                                                                                    End Sub,
                                                                                                                    TaskCreationOptions.AttachedToParent)
                                                                                                                    Throw New CustomException("Attached child1 faulted.")
                                                                              End Sub,
                                                                              TaskCreationOptions.AttachedToParent)
                                        End Sub)

      Try
         task1.Wait()
      Catch ae As AggregateException
         For Each ex In ae.Flatten().InnerExceptions
            If TypeOf ex Is CustomException Then
               Console.WriteLine(ex.Message)
            Else
               Throw
            End If
         Next
      End Try
   End Sub
End Module

Class CustomException : Inherits Exception
   Public Sub New(s As String)
      MyBase.New(s)
   End Sub
End Class
' The example displays the following output:
'       Attached child1 faulted.
'       Attached child2 faulted.

また、次の例のように、AggregateException.Flatten メソッドを使用して、単一の AggregateException インスタンス内の複数のタスクによってスローされた複数の AggregateException インスタンスから内部例外を再スローすることもできます。You can also use the AggregateException.Flatten method to rethrow the inner exceptions from multiple AggregateException instances thrown by multiple tasks in a single AggregateException instance, as the following example shows.

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
public class Example
{
   public static void Main()
   {
        try {
            ExecuteTasks();
        }
        catch (AggregateException ae) {
            foreach (var e in ae.InnerExceptions) {
                Console.WriteLine("{0}:\n   {1}", e.GetType().Name, e.Message);
            }
        }
   }

   static void ExecuteTasks()
   {
        // Assume this is a user-entered String.
        String path = @"C:\";
        List<Task> tasks = new List<Task>();

        tasks.Add(Task.Run(() => {
                             // This should throw an UnauthorizedAccessException.
                              return Directory.GetFiles(path, "*.txt",
                                                        SearchOption.AllDirectories);
                           }));

        tasks.Add(Task.Run(() => {
                              if (path == @"C:\")
                                 throw new ArgumentException("The system root is not a valid path.");
                              return new String[] { ".txt", ".dll", ".exe", ".bin", ".dat" };
                           }));

        tasks.Add(Task.Run(() => {
                               throw new NotImplementedException("This operation has not been implemented.");
                           }));

        try {
            Task.WaitAll(tasks.ToArray());
        }
        catch (AggregateException ae) {
            throw ae.Flatten();
        }
    }
}
// The example displays the following output:
//       UnauthorizedAccessException:
//          Access to the path 'C:\Documents and Settings' is denied.
//       ArgumentException:
//          The system root is not a valid path.
//       NotImplementedException:
//          This operation has not been implemented.
Imports System.Collections.Generic
Imports System.IO
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
       Try
          ExecuteTasks()
       Catch ae As AggregateException
          For Each e In ae.InnerExceptions
             Console.WriteLine("{0}:{2}   {1}", e.GetType().Name, e.Message,
                               vbCrLf)
          Next
       End Try
    End Sub

    Sub ExecuteTasks()
        ' Assume this is a user-entered String.
        Dim path = "C:\"
        Dim tasks As New List(Of Task)
        
        tasks.Add(Task.Run(Function()
                             ' This should throw an UnauthorizedAccessException.
                              Return Directory.GetFiles(path, "*.txt",
                                                        SearchOption.AllDirectories)
                           End Function))

        tasks.Add(Task.Run(Function()
                              If path = "C:\" Then
                                 Throw New ArgumentException("The system root is not a valid path.")
                              End If
                              Return { ".txt", ".dll", ".exe", ".bin", ".dat" }
                           End Function))

        tasks.Add(Task.Run(Sub()
                              Throw New NotImplementedException("This operation has not been implemented.")
                           End Sub))

        Try
            Task.WaitAll(tasks.ToArray)
        Catch ae As AggregateException
            Throw ae.Flatten()
        End Try
    End Sub
End Module
' The example displays the following output:
'       UnauthorizedAccessException:
'          Access to the path 'C:\Documents and Settings' is denied.
'       ArgumentException:
'          The system root is not a valid path.
'       NotImplementedException:
'          This operation has not been implemented.

デタッチされた子タスクの例外Exceptions from detached child tasks

既定では、子タスクはデタッチされた状態で作成されます。By default, child tasks are created as detached. デタッチされたタスクからスローされた例外は、処理されるか、直接の親に再スローされる必要があります。これらの例外は、アタッチされた子タスクが反映されるのとは異なる方法で、呼び出し元のスレッドに反映されます。Exceptions thrown from detached tasks must be handled or rethrown in the immediate parent task; they are not propagated back to the calling thread in the same way as attached child tasks propagated back. 最上位の親では、デタッチされた子からの例外を手動で再スローすることで、 AggregateException にラップして、呼び出し元のスレッドに反映させることができます。The topmost parent can manually rethrow an exception from a detached child to cause it to be wrapped in an AggregateException and propagated back to the calling thread.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var task1 = Task.Run(() => {
                       var nested1 = Task.Run(() => {
                                          throw new CustomException("Detached child task faulted.");
                                     });

          // Here the exception will be escalated back to the calling thread.
          // We could use try/catch here to prevent that.
          nested1.Wait();
      });

      try {
         task1.Wait();
      }
      catch (AggregateException ae) {
         foreach (var e in ae.Flatten().InnerExceptions) {
            if (e is CustomException) {
               Console.WriteLine(e.Message);
            }
         }
      }
   }
}

public class CustomException : Exception
{
   public CustomException(String message) : base(message)
   {}
}
// The example displays the following output:
//    Detached child task faulted.
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim task1 = Task.Run(Sub()
                              Dim nestedTask1 = Task.Run(Sub()
                                                            Throw New CustomException("Detached child task faulted.")
                                                         End Sub)
                              ' Here the exception will be escalated back to joining thread.
                              ' We could use try/catch here to prevent that.
                              nestedTask1.Wait()
                           End Sub)

      Try
          task1.Wait()
      Catch ae As AggregateException
          For Each ex In ae.Flatten().InnerExceptions
              If TypeOf ex Is CustomException Then
                  ' Recover from the exception. Here we just
                  ' print the message for demonstration purposes.
                  Console.WriteLine(ex.Message)
              End If
          Next
      End Try
   End Sub
End Module

Class CustomException : Inherits Exception
   Public Sub New(s As String)
      MyBase.New(s)
   End Sub
End Class
' The example displays the following output:
'       Detached child task faulted.

子タスク内の例外を確認するために継続を使用する場合でも、親タスクによって例外を確認する必要があります。Even if you use a continuation to observe an exception in a child task, the exception still must be observed by the parent task.

他の処理との連携によるキャンセル処理を示す例外Exceptions that indicate cooperative cancellation

タスク内のユーザー コードがキャンセル要求に応答する場合の正しいプロシージャは、その要求が伝えられたキャセル トークンに渡すために OperationCanceledException をスローすることです。When user code in a task responds to a cancellation request, the correct procedure is to throw an OperationCanceledException passing in the cancellation token on which the request was communicated. 例外が反映される前に、タスク インスタンスによって、例外内のトークンがそのタスクが作成されたときに渡されたトークンと比較されます。Before it attempts to propagate the exception, the task instance compares the token in the exception to the one that was passed to it when it was created. それらが同じである場合、タスクは TaskCanceledException でラップされた AggregateExceptionを反映します。これは、内部例外を調べると確認できます。If they are the same, the task propagates a TaskCanceledException wrapped in the AggregateException, and it can be seen when the inner exceptions are examined. ただし、呼び出し元のスレッドがタスクを待機していない場合、このような特定の例外は反映されません。However, if the calling thread is not waiting on the task, this specific exception will not be propagated. 詳細については、「タスクのキャンセル」をご覧ください。For more information, see Task Cancellation.

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;

var task1 = Task.Factory.StartNew(() =>
{
    CancellationToken ct = token;
    while (someCondition)
    {
        // Do some work...
        Thread.SpinWait(50000);
        ct.ThrowIfCancellationRequested();
    }
},
token);

// No waiting required.
tokenSource.Dispose();
Dim someCondition As Boolean = True
Dim tokenSource = New CancellationTokenSource()
Dim token = tokenSource.Token

Dim task1 = Task.Factory.StartNew(Sub()
                                      Dim ct As CancellationToken = token
                                      While someCondition = True
                                          ' Do some work...
                                          Thread.SpinWait(500000)
                                          ct.ThrowIfCancellationRequested()
                                      End While
                                  End Sub,
                                  token)

内部例外をフィルター処理する Handle メソッドの使用Using the handle method to filter inner exceptions

AggregateException.Handle メソッドを使用すると、"処理済み" として扱うことのできる例外をフィルターで除外できます。追加のロジックを使用する必要はありません。You can use the AggregateException.Handle method to filter out exceptions that you can treat as "handled" without using any further logic. AggregateException.Handle(Func<Exception,Boolean>) メソッドに用意されているユーザー デリゲートでは、例外の種類、その Message プロパティ、またはその例外に問題がないかどうかを判別できるその他の情報を調べることができます。In the user delegate that is supplied to the AggregateException.Handle(Func<Exception,Boolean>) method, you can examine the exception type, its Message property, or any other information about it that will let you determine whether it is benign. デリゲートが false を返す例外は、AggregateException メソッドが返された直後に新しい AggregateException.Handle インスタンスで再スローされます。Any exceptions for which the delegate returns false are rethrown in a new AggregateException instance immediately after the AggregateException.Handle method returns.

次の例は、機能的には、AggregateException.InnerExceptions コレクション内の各例外を調べるこのトピックの最初の例と同じです。The following example is functionally equivalent to the first example in this topic, which examines each exception in the AggregateException.InnerExceptions collection. 代わりに、この例外ハンドラーでは、例外ごとに AggregateException.Handle メソッドのオブジェクトを呼び出し、CustomException インスタンスでない例外のみを再スローしています。Instead, this exception handler calls the AggregateException.Handle method object for each exception, and only rethrows exceptions that are not CustomException instances.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var task1 = Task.Run( () => { throw new CustomException("This exception is expected!"); } );

      try {
          task1.Wait();
      }
      catch (AggregateException ae)
      {
         // Call the Handle method to handle the custom exception,
         // otherwise rethrow the exception.
         ae.Handle(ex => { if (ex is CustomException)
                             Console.WriteLine(ex.Message);
                          return ex is CustomException;
                        });
      }
   }
}

public class CustomException : Exception
{
   public CustomException(String message) : base(message)
   {}
}
// The example displays the following output:
//        This exception is expected!
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim task1 = Task.Run(Sub() Throw New CustomException("This exception is expected!"))

      Try
         task1.Wait()
      Catch ae As AggregateException
         ' Call the Handle method to handle the custom exception,
         ' otherwise rethrow the exception.
         ae.Handle(Function(e)
                      If TypeOf e Is CustomException Then
                         Console.WriteLine(e.Message)
                      End If
                      Return TypeOf e Is CustomException
                   End Function)
      End Try
   End Sub
End Module

Class CustomException : Inherits Exception
   Public Sub New(s As String)
      MyBase.New(s)
   End Sub
End Class
' The example displays the following output:
'       This exception is expected!

次の例はさらに複雑です。AggregateException.Handle メソッドを使用して、ファイルの列挙時に UnauthorizedAccessException 例外の特別な処理を行っています。The following is a more complete example that uses the AggregateException.Handle method to provide special handling for an UnauthorizedAccessException exception when enumerating files.

using System;
using System.IO;
using System.Threading.Tasks;

public class Example
{
    public static void Main()
    {
        // This should throw an UnauthorizedAccessException.
       try {
           var files = GetAllFiles(@"C:\");
           if (files != null)
              foreach (var file in files)
                 Console.WriteLine(file);
        }
        catch (AggregateException ae) {
           foreach (var ex in ae.InnerExceptions)
               Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message);
        }
        Console.WriteLine();

        // This should throw an ArgumentException.
        try {
           foreach (var s in GetAllFiles(""))
              Console.WriteLine(s);
        }
        catch (AggregateException ae) {
           foreach (var ex in ae.InnerExceptions)
               Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message);
        }
    }

    static string[] GetAllFiles(string path)
    {
       var task1 = Task.Run( () => Directory.GetFiles(path, "*.txt",
                                                      SearchOption.AllDirectories));

       try {
          return task1.Result;
       }
       catch (AggregateException ae) {
          ae.Handle( x => { // Handle an UnauthorizedAccessException
                            if (x is UnauthorizedAccessException) {
                                Console.WriteLine("You do not have permission to access all folders in this path.");
                                Console.WriteLine("See your network administrator or try another path.");
                            }
                            return x is UnauthorizedAccessException;
                          });
          return Array.Empty<String>();
       }
   }
}
// The example displays the following output:
//       You do not have permission to access all folders in this path.
//       See your network administrator or try another path.
//
//       ArgumentException: The path is not of a legal form.
Imports System.IO
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        ' This should throw an UnauthorizedAccessException.
       Try
           Dim files = GetAllFiles("C:\")
           If files IsNot Nothing Then
              For Each file In files
                 Console.WriteLine(file)
              Next
           End If
        Catch ae As AggregateException
           For Each ex In ae.InnerExceptions
               Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message)
           Next
        End Try
        Console.WriteLine()

       ' This should throw an ArgumentException.
        Try
           For Each s In GetAllFiles("")
              Console.WriteLine(s)
           Next
        Catch ae As AggregateException
           For Each ex In ae.InnerExceptions
               Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message)
           Next
        End Try
        Console.WriteLine()
    End Sub

    Function GetAllFiles(ByVal path As String) As String()
       Dim task1 = Task.Run( Function()
                                Return Directory.GetFiles(path, "*.txt",
                                                          SearchOption.AllDirectories)
                             End Function)
       Try
          Return task1.Result
       Catch ae As AggregateException
          ae.Handle( Function(x)
                        ' Handle an UnauthorizedAccessException
                        If TypeOf x Is UnauthorizedAccessException Then
                            Console.WriteLine("You do not have permission to access all folders in this path.")
                            Console.WriteLine("See your network administrator or try another path.")
                        End If
                        Return TypeOf x Is UnauthorizedAccessException
                     End Function)
       End Try
       Return Array.Empty(Of String)()
    End Function
End Module
' The example displays the following output:
'       You do not have permission to access all folders in this path.
'       See your network administrator or try another path.
'
'       ArgumentException: The path is not of a legal form.

Task.Exception プロパティによる例外の確認Observing exceptions by using the Task.Exception property

タスクが TaskStatus.Faulted 状態で完了した場合、その Exception プロパティを調べることで違反の原因となった例外を見つけることができます。If a task completes in the TaskStatus.Faulted state, its Exception property can be examined to discover which specific exception caused the fault. Exception プロパティを確認する場合は、次の例に示すように、継続元タスクが違反した場合にのみ実行される継続を使用することをお勧めします。A good way to observe the Exception property is to use a continuation that runs only if the antecedent task faults, as shown in the following example.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var task1 = Task.Run(() =>
                           { throw new CustomException("task1 faulted.");
      }).ContinueWith( t => { Console.WriteLine("{0}: {1}",
                                                t.Exception.InnerException.GetType().Name,
                                                t.Exception.InnerException.Message);
                            }, TaskContinuationOptions.OnlyOnFaulted);
      Thread.Sleep(500);
   }
}

public class CustomException : Exception
{
   public CustomException(String message) : base(message)
   {}
}
// The example displays output like the following:
//        CustomException: task1 faulted.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim task1 = Task.Factory.StartNew(Sub()
                                           Throw New CustomException("task1 faulted.")
                                        End Sub).
                  ContinueWith(Sub(t)
                                  Console.WriteLine("{0}: {1}",
                                                    t.Exception.InnerException.GetType().Name,
                                                    t.Exception.InnerException.Message)
                               End Sub, TaskContinuationOptions.OnlyOnFaulted)

      Thread.Sleep(500)
   End Sub
End Module

Class CustomException : Inherits Exception
   Public Sub New(s As String)
      MyBase.New(s)
   End Sub
End Class
' The example displays output like the following:
'       CustomException: task1 faulted.

実際のアプリケーションでは、継続のデリゲートで例外に関する詳細情報を記録して、新しいタスクを作成して例外から回復することが考えられます。In a real application, the continuation delegate could log detailed information about the exception and possibly spawn new tasks to recover from the exception.

UnobservedTaskException イベントUnobservedTaskException event

信頼関係のないプラグインをホストするときなど、シナリオによっては、問題のない例外がよく発生する場合や、難しすぎてすべてを手動で観察できなくなる場合があります。In some scenarios, such as when hosting untrusted plug-ins, benign exceptions might be common, and it might be too difficult to manually observe them all. このような場合、 TaskScheduler.UnobservedTaskException イベントを処理できます。In these cases, you can handle the TaskScheduler.UnobservedTaskException event. ハンドラーに渡される System.Threading.Tasks.UnobservedTaskExceptionEventArgs インスタンスを使用して、観察されない例外が連結しているスレッドに反映されないようにすることができます。The System.Threading.Tasks.UnobservedTaskExceptionEventArgs instance that is passed to your handler can be used to prevent the unobserved exception from being propagated back to the joining thread.

関連項目See also