다음을 통해 공유


방법: PLINQ 쿼리의 예외 처리

업데이트: 2010년 5월

이 항목의 첫 번째 예제에서는 PLINQ 쿼리를 실행할 때 이 쿼리에서 throw될 수 있는 System.AggregateException을 처리하는 방법을 보여 줍니다. 두 번째 예제에서는 대리자 내에서 예외가 throw되는 지점과 가능한 한 가까운 지점에 try-catch 블록을 배치하는 방법을 보여 줍니다. 이렇게 하면 예외가 발생하는 즉시 이를 catch하고 쿼리 실행을 계속할 수 있습니다. 조인하는 스레드까지 버블링할 수 있도록 예외가 허용되는 경우 예외가 발생한 후에도 쿼리에서 일부 항목을 계속하여 처리할 수 있습니다.

경우에 따라서는 PLINQ가 순차 실행을 진행하는 동안 예외가 발생했을 때 예외를 AggregateException에 래핑하지 않고 직접 전파할 수 있습니다. 또한 ThreadAbortException은 항상 직접 전파됩니다.

참고참고

"내 코드만"을 사용하는 경우 Visual Studio에서는 예외를 throw하는 줄에서 실행을 중단하고 예외가 사용자 코드를 통해 처리되지 않았음을 알리는 오류 메시지를 표시할 수 있습니다. 이는 크게 심각한 오류는 아닙니다.F5 키를 눌러 중단된 지점부터 실행을 계속하고 아래 예제에 나와 있는 것과 같은 예외 처리 동작을 확인할 수 있습니다.맨 처음 오류 지점에서 Visual Studio가 실행을 중단하지 않도록 하려면 도구, 옵션, 디버깅, 일반을 차례로 선택하고 "내 코드만" 확인란의 선택을 취소하기만 하면 됩니다.

이 예제는 사용법을 보여 주기 위한 것이며, 이에 상응하는 순차 LINQ to Objects 쿼리보다 실행 속도가 느릴 수 있습니다.속도 향상에 대한 자세한 내용은 PLINQ의 속도 향상 이해를 참조하십시오.

예제

이 예제에서는 쿼리를 실행하는 코드에 try-catch 블록을 배치하여 throw되는 System.AggregateException을 catch하는 방법을 보여 줍니다.

' 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
// 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.");
        }
    }
}

이 예제에서는 예외가 throw된 후 쿼리를 계속할 수 없습니다. 응용 프로그램 코드에서 예외를 catch할 때 PLINQ가 이미 모든 스레드에서 쿼리를 중지했기 때문입니다.

다음 예제에서는 대리자에 try-catch 블록을 배치하여 예외를 catch하고 쿼리 실행을 계속할 수 있도록 하는 방법을 보여 줍니다.

' 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
// 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);
    }
}

코드 컴파일

  • 이러한 예제를 컴파일하고 실행하려면 해당 예제를 PLINQ 데이터 샘플 예제에 복사하고 Main에서 해당 메서드를 호출합니다.

강력한 프로그래밍

프로그램 상태가 손상되지 않도록 예외를 처리하는 방법을 모르는 경우에는 예외를 catch하지 않습니다.

참고 항목

참조

ParallelEnumerable

개념

PLINQ(병렬 LINQ)

변경 기록

날짜

변경 내용

이유

2010년 5월

사용법과 속도 향상에 대한 설명을 추가했습니다.

고객 의견