Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In den folgenden Beispielen werden zwei Möglichkeiten dargestellt, eine PLINQ-Abfrage abzubrechen. Im ersten Beispiel wird gezeigt, wie Sie eine hauptsächlich aus Datendurchläufen bestehende Abfrage abbrechen. Im zweiten Beispiel wird gezeigt, wie Sie eine Abfrage abbrechen, die eine rechenintensive Benutzerfunktion enthält.
Hinweis |
|---|
Wenn "Nur eigenen Code" aktiviert ist, unterbricht Visual Studio die Ausführung in der Zeile, die die Ausnahme auslöst, und eine Fehlermeldung zu einer nicht vom Benutzercode behandelten Ausnahme wird angezeigt. Dieser Fehler hat keine Auswirkungen.Sie können F5 drücken, um den Vorgang fortzusetzen. In diesem Fall wird das in den unten stehenden Beispielen veranschaulichte Ausnahmebehandlungsverhalten angewendet.Um zu verhindern, dass Visual Studio die Ausführung beim ersten Fehler unterbricht, deaktivieren Sie das Kontrollkästchen "Nur eigenen Code" unter Extras, Optionen, Debugging, Allgemein. Dieses Beispiel soll die Verwendung veranschaulichen, und es wird möglicherweise nicht schneller als die entsprechende sequenzielle LINQ to Objects-Abfrage ausgeführt.Weitere Informationen über Geschwindigkeitssteigerungen finden Sie unter Grundlagen zur Beschleunigung in PLINQ. |
Beispiel
Class Program
Private Shared Sub Main(ByVal args As String())
Dim source As Integer() = Enumerable.Range(1, 10000000).ToArray()
Dim cs As New CancellationTokenSource()
' Start a new asynchronous task that will cancel the
' operation from another thread. Typically you would call
' Cancel() in response to a button click or some other
' user interface event.
Task.Factory.StartNew(Sub()
UserClicksTheCancelButton(cs)
End Sub)
Dim results As Integer() = Nothing
Try
results = (From num In source.AsParallel().WithCancellation(cs.Token) _
Where num Mod 3 = 0 _
Order By num Descending _
Select num).ToArray()
Catch e As OperationCanceledException
Console.WriteLine(e.Message)
Catch ae As AggregateException
If ae.InnerExceptions IsNot Nothing Then
For Each e As Exception In ae.InnerExceptions
Console.WriteLine(e.Message)
Next
End If
End Try
If results IsNot Nothing Then
For Each item In results
Console.WriteLine(item)
Next
End If
Console.WriteLine()
Console.ReadKey()
End Sub
Private Shared Sub UserClicksTheCancelButton(ByVal cs As CancellationTokenSource)
' Wait between 150 and 500 ms, then cancel.
' Adjust these values if necessary to make
' cancellation fire while query is still executing.
Dim rand As New Random()
Thread.Sleep(rand.[Next](150, 350))
cs.Cancel()
End Sub
End Class
namespace PLINQCancellation_1
{
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
int[] source = Enumerable.Range(1, 10000000).ToArray();
CancellationTokenSource cs = new CancellationTokenSource();
// Start a new asynchronous task that will cancel the
// operation from another thread. Typically you would call
// Cancel() in response to a button click or some other
// user interface event.
Task.Factory.StartNew(() =>
{
UserClicksTheCancelButton(cs);
});
int[] results = null;
try
{
results = (from num in source.AsParallel().WithCancellation(cs.Token)
where num % 3 == 0
orderby num descending
select num).ToArray();
}
catch (OperationCanceledException e)
{
Console.WriteLine(e.Message);
}
catch (AggregateException ae)
{
if (ae.InnerExceptions != null)
{
foreach (Exception e in ae.InnerExceptions)
Console.WriteLine(e.Message);
}
}
if (results != null)
{
foreach (var v in results)
Console.WriteLine(v);
}
Console.WriteLine();
Console.ReadKey();
}
static void UserClicksTheCancelButton(CancellationTokenSource cs)
{
// Wait between 150 and 500 ms, then cancel.
// Adjust these values if necessary to make
// cancellation fire while query is still executing.
Random rand = new Random();
Thread.Sleep(rand.Next(150, 350));
cs.Cancel();
}
}
}
Das PLINQ-Framework wandelt eine einzelne OperationCanceledException nicht in eine System.AggregateException um. Die OperationCanceledException muss in einem separaten catch-Block behandelt werden. Wenn ein oder mehrere Benutzerdelegaten eine OperationCanceledException(externalCT) (durch Verwendung eines externen System.Threading.CancellationToken) und keine andere Ausnahme auslösen und die Abfrage zudem als AsParallel().WithCancellation(externalCT) definiert wurde, gibt PLINQ eine einzelne OperationCanceledException (externalCT) anstelle einer System.AggregateException aus. Wenn jedoch ein Benutzerdelegat eine OperationCanceledException und ein anderer Delegat einen anderen Ausnahmetyp auslöst, werden beide Ausnahmen in eine AggregateException umgewandelt.
Die Faustregel für einen Abbruch lautet wie folgt:
Beim Abbruch eines Benutzerdelegaten sollten Sie PLINQ über das externe CancellationToken informieren und eine OperationCanceledException(externalCT) auslösen.
Wenn im Falle eines Abbruchs keine anderen Ausnahmen ausgelöst werden, sollten Sie eine OperationCanceledException anstelle einer AggregateException behandeln.
Im folgenden Beispiel wird gezeigt, wie Sie einen Abbruch behandeln, wann eine rechenintensive Funktion im Benutzercode enthalten ist.
Class Program2
Private Shared Sub Main(ByVal args As String())
Dim source As Integer() = Enumerable.Range(1, 10000000).ToArray()
Dim cs As New CancellationTokenSource()
' Start a new asynchronous task that will cancel the
' operation from another thread. Typically you would call
' Cancel() in response to a button click or some other
' user interface event.
Task.Factory.StartNew(Sub()
UserClicksTheCancelButton(cs)
End Sub)
Dim results As Double() = Nothing
Try
results = (From num In source.AsParallel().WithCancellation(cs.Token) _
Where num Mod 3 = 0 _
Select [Function](num, cs.Token)).ToArray()
Catch e As OperationCanceledException
Console.WriteLine(e.Message)
Catch ae As AggregateException
If ae.InnerExceptions IsNot Nothing Then
For Each e As Exception In ae.InnerExceptions
Console.WriteLine(e.Message)
Next
End If
End Try
If results IsNot Nothing Then
For Each item In results
Console.WriteLine(item)
Next
End If
Console.WriteLine()
Console.ReadKey()
End Sub
' A toy method to simulate work.
Private Shared Function [Function](ByVal n As Integer, ByVal ct As CancellationToken) As Double
' If work is expected to take longer than 1 ms
' then try to check cancellation status more
' often within that work.
For i As Integer = 0 To 4
' Work hard for approx 1 millisecond.
Thread.SpinWait(50000)
' Check for cancellation request.
If ct.IsCancellationRequested Then
Throw New OperationCanceledException(ct)
End If
Next
' Anything will do for our purposes.
Return Math.Sqrt(n)
End Function
Private Shared Sub UserClicksTheCancelButton(ByVal cs As CancellationTokenSource)
' Wait between 150 and 500 ms, then cancel.
' Adjust these values if necessary to make
' cancellation fire while query is still executing.
Dim rand As New Random()
Thread.Sleep(rand.[Next](150, 350))
Console.WriteLine("Press 'c' to cancel")
If Console.ReadKey().KeyChar = "c"c Then
cs.Cancel()
End If
End Sub
End Class
namespace PLINQCancellation_2
{
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
int[] source = Enumerable.Range(1, 10000000).ToArray();
CancellationTokenSource cs = new CancellationTokenSource();
// Start a new asynchronous task that will cancel the
// operation from another thread. Typically you would call
// Cancel() in response to a button click or some other
// user interface event.
Task.Factory.StartNew(() =>
{
UserClicksTheCancelButton(cs);
});
double[] results = null;
try
{
results = (from num in source.AsParallel().WithCancellation(cs.Token)
where num % 3 == 0
select Function(num, cs.Token)).ToArray();
}
catch (OperationCanceledException e)
{
Console.WriteLine(e.Message);
}
catch (AggregateException ae)
{
if (ae.InnerExceptions != null)
{
foreach (Exception e in ae.InnerExceptions)
Console.WriteLine(e.Message);
}
}
if (results != null)
{
foreach (var v in results)
Console.WriteLine(v);
}
Console.WriteLine();
Console.ReadKey();
}
// A toy method to simulate work.
static double Function(int n, CancellationToken ct)
{
// If work is expected to take longer than 1 ms
// then try to check cancellation status more
// often within that work.
for (int i = 0; i < 5; i++)
{
// Work hard for approx 1 millisecond.
Thread.SpinWait(50000);
// Check for cancellation request.
ct.ThrowIfCancellationRequested();
}
// Anything will do for our purposes.
return Math.Sqrt(n);
}
static void UserClicksTheCancelButton(CancellationTokenSource cs)
{
// Wait between 150 and 500 ms, then cancel.
// Adjust these values if necessary to make
// cancellation fire while query is still executing.
Random rand = new Random();
Thread.Sleep(rand.Next(150, 350));
Console.WriteLine("Press 'c' to cancel");
if (Console.ReadKey().KeyChar == 'c')
cs.Cancel();
}
}
}
Wenn Sie den Abbruch im Benutzercode behandeln, müssen Sie WithCancellation<TSource> in der Abfragedefinition nicht verwenden. Dies ist jedoch empfehlenswert, da WithCancellation<TSource> keine Auswirkungen auf die Abfrageleistung hat, der Abbruch so jedoch von Abfrageoperatoren und dem Benutzercode behandelt werden kann.
Um die Reaktionsgeschwindigkeit des Systems sicherzustellen, empfiehlt es sich, einmal pro Millisekunde nach einem Abbruch zu suchen. Es sind jedoch Intervalle von bis zu 10 Millisekunden zulässig. Dieses Intervall hat in der Regel keine negativen Auswirkungen auf die Leistung des Codes.
Wenn ein Enumerator verworfen wird, z. B. wenn Code aus einer foreach-Schleife (For Each in Visual Basic) ausbricht, die Abfrageergebnisse durchläuft, wird die Abfrage abgebrochen, aber keine Ausnahme ausgelöst.
Siehe auch
Referenz
Konzepte
Änderungsprotokoll
Datum |
Versionsgeschichte |
Grund |
|---|---|---|
|
Mai 2010 |
Hinweis bezüglich Verwendung und Geschwindigkeitssteigerung hinzugefügt. |
Kundenfeedback. |
Hinweis