예외 처리(작업 병렬 라이브러리)Exception handling (Task Parallel Library)

작업 내에서 실행되는 사용자 코드에 의해 throw된 처리되지 않은 예외는 이 항목의 뒷부분에서 설명하는 특정 시나리오를 제외하고는 호출 스레드로 다시 전파됩니다.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 메서드 중 하나를 사용할 때 예외가 전파되며 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. 어떤 작업이 연결된 자식 작업의 부모인 경우 또는 여러 작업에서 대기 중인 경우, 여러 개의 예외가 throw될 수 있습니다.If a task is the parent of attached child tasks, or if you are waiting on multiple tasks, multiple exceptions could be thrown.

모든 예외를 호출 스레드로 다시 전파하기 위해 작업 인프라가 이러한 예외를 AggregateException 인스턴스에서 래핑합니다.To propagate all the exceptions back to the calling thread, the Task infrastructure wraps them in an AggregateException instance. AggregateException 예외에는 InnerExceptions 속성이 있으며 이 속성을 열거하면 throw된 모든 원래 예외를 확인하고 각 예외를 개별적으로 처리하거나 처리하지 않을 수 있습니다.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.

하나의 예외만 throw된 경우 다음 예제와 같이 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 을 catch하고 내부 예외를 관찰하지 않으면 처리되지 않은 예외를 방지할 수 있습니다.You could avoid an unhandled exception by just catching the AggregateException and not observing any of the inner exceptions. 하지만 이 방법은 기본 Exception 유형을 비병렬 시나리오에서 catch하는 것과 유사하기 때문에 사용하지 않는 것이 좋습니다.However, we recommend that you do not do this because it is analogous to catching the base Exception type in non-parallel scenarios. 복구하기 위한 특정 작업을 수행하지 않고 예외를 catch하려면 프로그램을 결정할 수 없는 상태 그대로 두면 됩니다.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

작업에 예외를 throw하는 연결된 자식 작업이 있는 경우 해당 예외가 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.Wait, WaitAny 또는 WaitAll 메서드에서 catch된 AggregateException 예외의 InnerExceptions 속성에는 오류를 발생시킨 원래 예외가 아니라 하나 이상의 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 예외를 반복할 필요가 없도록 하려면 AggregateException.InnerExceptions 속성에 원래 예외가 포함되도록 Flatten 메서드를 사용하여 중첩된 모든 AggregateException 예외를 제거합니다.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 인스턴스가 하나의 루프에서 결합되고 처리됩니다.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 인스턴스에서 여러 작업에 의해 throw된 여러 AggregateException 인스턴스의 내부 예외를 다시 throw할 수 있습니다.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. 분리된 작업에서 throw된 예외는 직계 부모 작업에서 처리되거나 다시 throw되어야 하지만, 연결된 자식 작업이 다시 전파되는 것과 동일한 방식으로 호출 스레드에 다시 전파되지는 않습니다.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. 최상위 부모는 분리된 자식의 예외를 수동으로 다시 throw하여 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 을 throw하는 것입니다.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)

핸들 메서드를 사용하여 내부 예외 필터링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.Handle 메서드에서 반환한 직후 새 AggregateException 인스턴스에서 다시 throw됩니다.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 인스턴스가 아닌 예외만 다시 throw합니다.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