방법: PLINQ 쿼리의 예외 처리How to: Handle Exceptions in a PLINQ Query

이 항목의 첫 번째 예제에서는 실행될 때 PLINQ 쿼리에서 throw될 수 있는 System.AggregateException을 처리하는 방법을 보여줍니다.The first example in this topic shows how to handle the System.AggregateException that can be thrown from a PLINQ query when it executes. 두 번째 예제에서는 대리자 내에서 예외가 throw될 위치에 가능한 한 가깝게 try-catch 블록을 넣는 방법을 보여줍니다.The second example shows how to put try-catch blocks within delegates, as close as possible to where the exception will be thrown. 이 방법으로 예외가 발생한 즉시 예외를 catch하고 쿼리 실행을 계속할 수 있습니다.In this way, you can catch them as soon as they occur and possibly continue query execution. 예외가 가입된 스레드로 다시 버블 업될 수 있는 경우 예외가 발생한 후에도 쿼리에서 일부 항목을 계속 처리할 수 있습니다.When exceptions are allowed to bubble up back to the joining thread, then it is possible that a query may continue to process some items after the exception is raised.

PLINQ가 순차적 실행으로 대체되고 예외가 발생하는 일부 경우에는 예외가 직접 전파되며 AggregateException으로 래핑되지 않을 수 있습니다.In some cases when PLINQ falls back to sequential execution, and an exception occurs, the exception may be propagated directly, and not wrapped in an AggregateException. ThreadAbortException은 항상 직접 전파됩니다.Also, ThreadAbortExceptions are always propagated directly.

참고

“내 코드만”이 사용하도록 설정된 경우 Visual Studio가 예외를 throw하는 줄에서 중단하고 “예외가 사용자 코드에서 처리되지 않았다”는 오류 메시지를 표시합니다.When "Just My Code" is enabled, Visual Studio 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 from it, and see the exception-handling behavior that is demonstrated in the examples below. 첫 번째 오류 지점에서 Visual Studio가 실행을 중단하지 않도록 하려면 도구, 옵션, 디버깅, 일반을 차례로 선택하고 “내 코드만” 확인란의 선택을 취소하기만 하면 됩니다.To prevent Visual Studio from breaking on the first error, just uncheck the "Just My Code" checkbox under Tools, Options, Debugging, General.

이 예제는 사용법을 보여 주기 위한 것이며, 동일한 순차 LINQ to Objects 쿼리보다 빠르게 실행되지 않을 수도 있습니다.This example is intended to demonstrate usage, and might not run faster than the equivalent sequential LINQ to Objects query. 속도 향상에 대한 자세한 내용은 PLINQ의 속도 향상 이해를 참조하세요.For more information about speedup, see Understanding Speedup in PLINQ.

Example

이 예제는 throw된 System.AggregateException을 catch하는 쿼리를 실행하는 코드 주위에 try-catch 블록을 넣는 방법을 보여줍니다.This example shows how to put the try-catch blocks around the code that executes the query to catch any System.AggregateExceptions that are thrown.

// Paste into PLINQDataSample class.
static void PLINQExceptions_1()
{
    // Using the raw string array here. See PLINQ Data Sample.
    string[] customers = GetCustomersAsStrings().ToArray();

    // First, we must simulate some currupt input.
    customers[54] = "###";

    var parallelQuery = from cust in customers.AsParallel()
                        let fields = cust.Split(',')
                        where fields[3].StartsWith("C") //throw indexoutofrange
                        select new { city = fields[3], thread = Thread.CurrentThread.ManagedThreadId };
    try
    {
        // We use ForAll although it doesn't really improve performance
        // since all output is serialized through the Console.
        parallelQuery.ForAll(e => Console.WriteLine("City: {0}, Thread:{1}", e.city, e.thread));
    }

    // In this design, we stop query processing when the exception occurs.
    catch (AggregateException e)
    {
        foreach (var ex in e.InnerExceptions)
        {
            Console.WriteLine(ex.Message);
            if (ex is IndexOutOfRangeException)
                Console.WriteLine("The data source is corrupt. Query stopped.");
        }
    }
}
' Paste into PLINQDataSample class
Shared Sub PLINQExceptions_1()

    ' Using the raw string array here. See PLINQ Data Sample.
    Dim customers As String() = GetCustomersAsStrings().ToArray()

    ' First, we must simulate some currupt input.
    customers(20) = "###"

    'throws indexoutofrange
    Dim query = From cust In customers.AsParallel() _
        Let fields = cust.Split(","c) _
        Where fields(3).StartsWith("C") _
        Select fields
    Try
        ' We use ForAll although it doesn't really improve performance
        ' since all output is serialized through the Console.
        query.ForAll(Sub(e)
                         Console.WriteLine("City: {0}, Thread:{1}")
                     End Sub)
    Catch e As AggregateException

        ' In this design, we stop query processing when the exception occurs.
        For Each ex In e.InnerExceptions
            Console.WriteLine(ex.Message)
            If TypeOf ex Is IndexOutOfRangeException Then
                Console.WriteLine("The data source is corrupt. Query stopped.")
            End If
        Next
    End Try
End Sub

이 예제에서는 예외가 throw된 후 쿼리를 계속할 수 없습니다.In this example, the query cannot continue after the exception is thrown. 애플리케이션 코드가 예외를 catch할 때까지 PLINQ가 모든 스레드에서 쿼리를 이미 중지했습니다.By the time your application code catches the exception, PLINQ has already stopped the query on all threads.

Example

다음 예제는 예외를 catch하고 쿼리 실행을 계속할 수 있도록 대리자에 try-catch 블록을 넣는 방법을 보여줍니다.The following example shows how to put a try-catch block in a delegate to make it possible to catch an exception and continue with the query execution.

// Paste into PLINQDataSample class.
static void PLINQExceptions_2()
{

    var customers = GetCustomersAsStrings().ToArray();
    // Using the raw string array here.
    // First, we must simulate some currupt input
    customers[54] = "###";

    // Create a delegate with a lambda expression.
    // Assume that in this app, we expect malformed data
    // occasionally and by design we just report it and continue.
    Func<string[], string, bool> isTrue = (f, c) =>
    {
        try
        {
            string s = f[3];
            return s.StartsWith(c);
        }
        catch (IndexOutOfRangeException e)
        {
            Console.WriteLine("Malformed cust: {0}", f);
            return false;
        }
    };

    // Using the raw string array here
    var parallelQuery = from cust in customers.AsParallel()
                        let fields = cust.Split(',')
                        where isTrue(fields, "C") //use a named delegate with a try-catch
                        select new { city = fields[3] };
    try
    {
        // We use ForAll although it doesn't really improve performance
        // since all output must be serialized through the Console.
        parallelQuery.ForAll(e => Console.WriteLine(e.city));
    }

    // IndexOutOfRangeException will not bubble up      
    // because we handle it where it is thrown.
    catch (AggregateException e)
    {
        foreach (var ex in e.InnerExceptions)
            Console.WriteLine(ex.Message);
    }
}
' Paste into PLINQDataSample class
Shared Sub PLINQExceptions_2()

    Dim customers() = GetCustomersAsStrings().ToArray()
    ' Using the raw string array here.
    ' First, we must simulate some currupt input
    customers(20) = "###"

    ' Create a delegate with a lambda expression.
    ' Assume that in this app, we expect malformed data
    ' occasionally and by design we just report it and continue.
    Dim isTrue As Func(Of String(), String, Boolean) = Function(f, c)

                                                           Try

                                                               Dim s As String = f(3)
                                                               Return s.StartsWith(c)

                                                           Catch e As IndexOutOfRangeException

                                                               Console.WriteLine("Malformed cust: {0}", f)
                                                               Return False
                                                           End Try
                                                       End Function

    ' Using the raw string array here
    Dim query = From cust In customers.AsParallel()
                        Let fields = cust.Split(","c)
                        Where isTrue(fields, "C")
                       Select New With {.City = fields(3)}
    Try
        ' We use ForAll although it doesn't really improve performance
        ' since all output must be serialized through the Console.
        query.ForAll(Sub(e) Console.WriteLine(e.city))


        ' IndexOutOfRangeException will not bubble up      
        ' because we handle it where it is thrown.
    Catch e As AggregateException
        For Each ex In e.InnerExceptions
            Console.WriteLine(ex.Message)
        Next
    End Try
End Sub

코드 컴파일Compiling the Code

  • 이러한 예제를 컴파일하고 실행하려면 PLINQ 데이터 샘플 예제에 복사하고 Main에서 메서드를 호출합니다.To compile and run these examples, copy them into the PLINQ Data Sample example and call the method from Main.

강력한 프로그래밍Robust Programming

프로그램 상태를 손상시키지 않도록 처리 방법을 아는 경우가 아니면 예외를 catch하지 마세요.Do not catch an exception unless you know how to handle it so that you do not corrupt the state of your program.

참고 항목See also