作法:處理 PLINQ 查詢中的例外狀況How to: Handle Exceptions in a PLINQ Query

此主題中的第一個範例顯示如何處理當執行 PLINQ 查詢時可從它擲回的 System.AggregateExceptionThe first example in this topic shows how to handle the System.AggregateException that can be thrown from a PLINQ query when it executes. 第二個範例顯示如何將 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. 如此一來,您可以在發生例外狀況時立即攔截它們,並可能繼續執行查詢。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.

注意

啟用 [Just My Code] 時,Visual Studio 會在擲回例外狀況的字行上中斷,並顯示錯誤訊息,指出「使用者程式碼未處理例外狀況」。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 在遇到第一個錯誤時就中斷,只要取消核取 [工具]、[選項]、[偵錯]、[一般] 下的 [Just My Code] 核取方塊即可。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

此範例會示範如何在執行查詢的程式碼周圍放置 try-catch 區塊,以攔截任何擲回的 System.AggregateExceptionThis 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

在此範例中,擲回例外狀況之後查詢無法繼續。In this example, the query cannot continue after the exception is thrown. 應用程式程式碼攔截到例外狀況時,PLINQ 已經停止所有執行緒上的查詢。By the time your application code catches the exception, PLINQ has already stopped the query on all threads.

範例Example

下列範例會示範如何將 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

除非您知道如何處理,否則請勿攔截例外狀況,這樣您才不會損毀您的程式狀態。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