Procedura: gestire le eccezioni in una query PLINQ
Il primo esempio di questo argomento mostra come gestire l'oggetto System.AggregateException che può essere generato da una query PLINQ quando viene eseguita. Nel secondo esempio viene mostrato come inserire blocchi try-catch all'interno dei delegati, nel punto più vicino possibile a quello in cui verranno generate le eccezioni. In questo modo sarà possibile rilevarle nel momento in cui si verificano e, in alcuni casi, continuare l'esecuzione della query. Quando alle eccezioni è consentita la propagazione fino al thread di unione, è possibile che una query continui a elaborare alcuni elementi dopo la generazione dell'eccezione.
In alcuni casi, quando PLINQ esegue il fallback all'esecuzione sequenziale e si verifica un'eccezione, l'eccezione può essere propagata direttamente e non viene eseguito il wrapping in AggregateException. Inoltre, le eccezioni ThreadAbortException vengono sempre propagate direttamente.
Nota |
---|
Quando viene abilitato "Just My Code", Visual Studio si interromperà in corrispondenza della riga che genera l'eccezione e in cui viene visualizzato un messaggio di errore che indica che l'eccezione non è gestita dal codice utente. Questo errore è benigno.È possibile premere F5 per continuare e visualizzare il comportamento di gestione delle eccezioni illustrato negli esempi riportati di seguito.Per impedire l'interruzione di Visual Studio al primo errore, deselezionare semplicemente la casella di controllo "Just My Code" in Strumenti, Opzioni, Debug, Generale. Lo scopo di questo esempio è dimostrare l'utilizzo e potrebbe non essere eseguito più velocemente dell'equivalente query LINQ to Objects sequenziale.Per ulteriori informazioni sull'aumento di velocità, vedere Informazioni sull'aumento di velocità in PLINQ. |
Esempio
In questo esempio viene mostrato come aggiungere i blocchi try-catch nel codice che esegue la query per rilevare qualsiasi oggetto System.AggregateException eventualmente generato.
' 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.");
}
}
}
In questo esempio, la query non può continuare dopo la generazione dell'eccezione. Quando il codice dell'applicazione rileverà l'eccezione, PLINQ avrà già interrotto la query in tutti i thread.
Nell'esempio seguente viene mostrato come aggiungere un blocco try-catch in un delegato per consentire il rilevamento di un'eccezione senza tuttavia interrompere l'esecuzione della query.
' 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);
}
}
Compilazione del codice
- Per compilare ed eseguire questi esempi, copiarli nell'esempio PLINQ Data Sample e chiamare il metodo da Main.
Programmazione efficiente
Rilevare soltanto le eccezioni che si è in grado di gestire in modo tale da non danneggiare lo stato del programma.
Vedere anche
Riferimenti
Concetti
Cronologia delle modifiche
Data |
Cronologia |
Motivo |
---|---|---|
Maggio 2010 |
Aggiunta nota sull'utilizzo rispetto all'aumento di velocità. |
Commenti e suggerimenti dei clienti. |