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.
Bei der asynchronen Programmierung ist es üblich, dass ein asynchroner Vorgang beim Abschluss einen zweiten Vorgang aufruft. Fortsetzungen ermöglichen absteigenden Vorgängen die Nutzung der Ergebnisse des ersten Vorgangs. Traditionell wurden Fortsetzungen mithilfe von Rückrufmethoden durchgeführt. In der Task Parallel Library (TPL) wird die gleiche Funktionalität von Fortsetzungsaufgaben bereitgestellt. Eine Fortsetzungsaufgabe (auch als Fortsetzung bezeichnet) ist eine asynchrone Aufgabe, die von einer anderen Aufgabe, dem sogenannten Antezedens, aufgerufen wird, wenn das Antezedens abgeschlossen ist.
Fortsetzungen sind relativ einfach zu bedienen, sind aber dennoch leistungsfähig und flexibel. Beispielsweise können Sie folgende Aktionen ausführen:
- Übergeben von Daten vom Vorgänger an die Fortsetzung
- Geben Sie die genauen Bedingungen an, unter denen die Fortsetzung aufgerufen oder nicht aufgerufen wird.
- Abbrechen einer Fortsetzung vor dem Start oder kooperativ während ihrer Ausführung
- Geben Sie Hinweise dazu an, wie die Fortsetzung geplant werden soll.
- Rufen Sie mehrere Fortsetzungen aus demselben Vorgänger auf.
- Aufrufen einer Fortsetzung, wenn alle Vorgänger abgeschlossen werden oder einer von mehreren Vorgängern abgeschlossen wird
- Verketten von Fortsetzungen der Reihe nach auf eine beliebige Länge
- Beheben von durch den Vorgänger ausgelösten Ausnahmen mithilfe einer Fortsetzung
Über Fortsetzungen
Eine Fortsetzung ist eine Aufgabe, die im WaitingForActivation-Zustand erstellt wird. Sie wird automatisch aktiviert, wenn ihre Vorgängeraufgaben abgeschlossen werden. Das Aufrufen von Task.Start für eine Fortsetzung in Benutzercode löst eine System.InvalidOperationException -Ausnahme aus.
Eine Fortsetzung ist selbst ein Task und blockiert nicht den Thread, auf dem sie gestartet wird. Rufen Sie die Task.Wait-Methode auf, um zu blockieren, bis die Fortsetzungsaufgabe abgeschlossen ist.
Erstellen einer Fortsetzung für einen einzelnen Vorgänger
Sie erstellen eine Fortsetzung, die ausgeführt wird, wenn der Vorgänger abgeschlossen ist, indem Sie die Task.ContinueWith-Methode aufrufen. Das folgende Beispiel zeigt das grundlegende Muster (aus Gründen der Übersichtlichkeit wird die Ausnahmebehandlung weggelassen). Er führt eine vorerkennte Aufgabe taskA
aus, die ein DayOfWeek Objekt zurückgibt, das den Namen des aktuellen Wochentags angibt. Nach Abschluss von taskA
stellt antecedent
die Ergebnisse in der ContinueWith
-Fortsetzungsmethode dar. Das Ergebnis der vorhergehenden Aufgabe wird in die Konsole geschrieben.
using System;
using System.Threading.Tasks;
public class SimpleExample
{
public static async Task Main()
{
// Declare, assign, and start the antecedent task.
Task<DayOfWeek> taskA = Task.Run(() => DateTime.Today.DayOfWeek);
// Execute the continuation when the antecedent finishes.
await taskA.ContinueWith(antecedent => Console.WriteLine($"Today is {antecedent.Result}."));
}
}
// The example displays the following output:
// Today is Monday.
Imports System.Threading.Tasks
Module Example
Public Sub Main()
' Execute the antecedent.
Dim taskA As Task(Of DayOfWeek) = Task.Run(Function() DateTime.Today.DayOfWeek)
' Execute the continuation when the antecedent finishes.
Dim continuation As Task = taskA.ContinueWith(Sub(antecedent)
Console.WriteLine("Today is {0}.", antecedent.Result)
End Sub)
continuation.Wait()
End Sub
End Module
' The example displays output like the following output:
' Today is Monday.
Erstellen einer Fortsetzung für mehrere Vorgänger
Sie können auch eine Fortsetzung erstellen, die ausgeführt wird, wenn eine oder alle Gruppen von Aufgaben abgeschlossen wurden. Um eine Fortsetzung auszuführen, wenn alle vorerkennten Aufgaben abgeschlossen wurden, können Sie die statische (Shared
in Visual Basic) Task.WhenAll -Methode oder die Instanzmethode TaskFactory.ContinueWhenAll aufrufen. Um eine Fortsetzung auszuführen, wenn eine der vorerkennten Aufgaben abgeschlossen ist, können Sie die statische (Shared
in Visual Basic) Task.WhenAny -Methode oder die Instanzmethode TaskFactory.ContinueWhenAny aufrufen.
Aufrufe der Überladungen Task.WhenAll und Task.WhenAny blockieren nicht den aufrufenden Thread. In der Regel rufen Sie jedoch alle Methoden außer den Task.WhenAll(IEnumerable<Task>) und Task.WhenAll(Task[]) Methoden auf, um die zurückgegebene Task<TResult>.Result-Eigenschaft zu erhalten, wodurch der aufrufende Thread blockiert wird.
Im folgenden Beispiel wird die Task.WhenAll(IEnumerable<Task>)-Methode aufgerufen, um eine Fortsetzungsaufgabe zu erstellen, die die Ergebnisse ihrer 10 vorhergehenden Aufgaben widerspiegelt. Jede Vorgängeraufgabe errechnet das Quadrat eines Indexwerts aus dem Bereich von 1 bis 10. Wenn die Vorgänger erfolgreich ausgeführt werden (also ihre Task.Status -Eigenschaft TaskStatus.RanToCompletionist), stellt die Task<TResult>.Result -Eigenschaft der Fortsetzung ein Array der Task<TResult>.Result -Werte dar, die von den einzelnen Vorgängern zurückgegeben wurden. Das Beispiel fügt sie hinzu, um die Summe von Quadraten für alle Zahlen zwischen 1 und 10 zu berechnen:
using System.Collections.Generic;
using System;
using System.Threading.Tasks;
public class WhenAllExample
{
public static async Task Main()
{
var tasks = new List<Task<int>>();
for (int ctr = 1; ctr <= 10; ctr++)
{
int baseValue = ctr;
tasks.Add(Task.Factory.StartNew(b => (int)b! * (int)b, baseValue));
}
var results = await Task.WhenAll(tasks);
int sum = 0;
for (int ctr = 0; ctr <= results.Length - 1; ctr++)
{
var result = results[ctr];
Console.Write($"{result} {((ctr == results.Length - 1) ? "=" : "+")} ");
sum += result;
}
Console.WriteLine(sum);
}
}
// The example displays the similar output:
// 1 + 4 + 9 + 16 + 25 + 36 + 49 + 64 + 81 + 100 = 385
Imports System.Collections.Generic
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim tasks As New List(Of Task(Of Integer))()
For ctr As Integer = 1 To 10
Dim baseValue As Integer = ctr
tasks.Add(Task.Factory.StartNew(Function(b)
Dim i As Integer = CInt(b)
Return i * i
End Function, baseValue))
Next
Dim continuation = Task.WhenAll(tasks)
Dim sum As Long = 0
For ctr As Integer = 0 To continuation.Result.Length - 1
Console.Write("{0} {1} ", continuation.Result(ctr),
If(ctr = continuation.Result.Length - 1, "=", "+"))
sum += continuation.Result(ctr)
Next
Console.WriteLine(sum)
End Sub
End Module
' The example displays the following output:
' 1 + 4 + 9 + 16 + 25 + 36 + 49 + 64 + 81 + 100 = 385
Fortsetzungsoptionen
Wenn Sie eine Fortsetzung mit einer einzelnen Aufgabe erstellen, können Sie eine ContinueWith Überladung verwenden, die einen System.Threading.Tasks.TaskContinuationOptions Enumerationswert verwendet, um die Bedingungen anzugeben, unter denen die Fortsetzung beginnt. Sie können beispielsweise angeben, dass die Fortsetzung nur ausgeführt werden soll, wenn der Vorgänger erfolgreich abgeschlossen wird, oder nur, wenn er in einem Fehlerzustand abgeschlossen wird. Falls die Bedingung nicht zutrifft, wenn der Vorgänger bereit ist, die Fortsetzung aufzurufen, wechselt die Fortsetzung unmittelbar in den TaskStatus.Canceled Zustand und kann später nicht gestartet werden.
Viele Fortsetzungsmethoden für mehrere Aufgaben, z. B. Überladungen der TaskFactory.ContinueWhenAll Methode, enthalten auch einen System.Threading.Tasks.TaskContinuationOptions Parameter. Es ist aber nur eine Teilmenge der System.Threading.Tasks.TaskContinuationOptions Aufzählungselemente gültig. Sie können System.Threading.Tasks.TaskContinuationOptions Werte angeben, die über Entsprechungen in der System.Threading.Tasks.TaskCreationOptions Aufzählung verfügen, wie TaskContinuationOptions.AttachedToParent, TaskContinuationOptions.LongRunning und TaskContinuationOptions.PreferFairness. Wenn Sie eine der Optionen NotOn
oder OnlyOn
mit einer Fortsetzung mehrerer Aufgaben angeben, wird zur Laufzeit eine ArgumentOutOfRangeException Ausnahme ausgelöst.
Weitere Informationen zu Optionen für die Aufgabenfortsetzung finden Sie im TaskContinuationOptions Artikel.
Übergeben von Daten an eine Fortsetzung
Die Task.ContinueWith-Methode übergibt einen Verweis auf den Vorgänger als Argument an den Benutzerdelegaten der Fortsetzung. Wenn das vorausgehende Objekt ein System.Threading.Tasks.Task<TResult> Objekt ist und die Aufgabe bis zum Abschluss ausgeführt wurde, kann die Fortsetzung auf die Task<TResult>.Result Eigenschaft der Aufgabe zugreifen.
Die Task<TResult>.Result -Eigenschaft ist bis zum Abschluss der Aufgabe blockiert. Wenn die Aufgabe jedoch abgebrochen oder fehlerhaft war, löst der Versuch, auf die Result Eigenschaft zuzugreifen, eine AggregateException Ausnahme aus. Sie können dieses Problem vermeiden, indem Sie die OnlyOnRanToCompletion Option verwenden, wie im folgenden Beispiel gezeigt:
using System;
using System.Threading.Tasks;
public class ResultExample
{
public static async Task Main()
{
var task = Task.Run(
() =>
{
DateTime date = DateTime.Now;
return date.Hour > 17
? "evening"
: date.Hour > 12
? "afternoon"
: "morning";
});
await task.ContinueWith(
antecedent =>
{
Console.WriteLine($"Good {antecedent.Result}!");
Console.WriteLine($"And how are you this fine {antecedent.Result}?");
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
}
// The example displays the similar output:
// Good afternoon!
// And how are you this fine afternoon?
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim t = Task.Run(Function()
Dim dat As DateTime = DateTime.Now
If dat = DateTime.MinValue Then
Throw New ArgumentException("The clock is not working.")
End If
If dat.Hour > 17 Then
Return "evening"
Else If dat.Hour > 12 Then
Return "afternoon"
Else
Return "morning"
End If
End Function)
Dim c = t.ContinueWith(Sub(antecedent)
Console.WriteLine("Good {0}!",
antecedent.Result)
Console.WriteLine("And how are you this fine {0}?",
antecedent.Result)
End Sub, TaskContinuationOptions.OnlyOnRanToCompletion)
c.Wait()
End Sub
End Module
' The example displays output like the following:
' Good afternoon!
' And how are you this fine afternoon?
Wenn die Fortsetzung auch dann ausgeführt werden soll, wenn der Vorgänger nicht erfolgreich bis zum Abschluss ausgeführt wurde, müssen Sie Vorkehrungen gegen die Ausnahme treffen. Ein Ansatz besteht darin, die Eigenschaft Task.Status des Antezedens zu testen und nur dann auf die Eigenschaft Result zuzugreifen, wenn der Status nicht Faulted oder Canceled ist. Sie können jedoch auch die Exception -Eigenschaft des Vorgängers überprüfen. Weitere Informationen finden Sie unter "Ausnahmebehandlung". Im folgenden Beispiel wird das vorangehende Beispiel so geändert, dass nur dann auf die Task<TResult>.Result Eigenschaft eines vorgegangenen Objekts zugegriffen wird, wenn der Status lautet TaskStatus.RanToCompletion:
using System;
using System.Threading.Tasks;
public class ResultTwoExample
{
public static async Task Main() =>
await Task.Run(
() =>
{
DateTime date = DateTime.Now;
return date.Hour > 17
? "evening"
: date.Hour > 12
? "afternoon"
: "morning";
})
.ContinueWith(
antecedent =>
{
if (antecedent.Status == TaskStatus.RanToCompletion)
{
Console.WriteLine($"Good {antecedent.Result}!");
Console.WriteLine($"And how are you this fine {antecedent.Result}?");
}
else if (antecedent.Status == TaskStatus.Faulted)
{
Console.WriteLine(antecedent.Exception!.GetBaseException().Message);
}
});
}
// The example displays output like the following:
// Good afternoon!
// And how are you this fine afternoon?
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim t = Task.Run(Function()
Dim dat As DateTime = DateTime.Now
If dat = DateTime.MinValue Then
Throw New ArgumentException("The clock is not working.")
End If
If dat.Hour > 17 Then
Return "evening"
Else If dat.Hour > 12 Then
Return "afternoon"
Else
Return "morning"
End If
End Function)
Dim c = t.ContinueWith(Sub(antecedent)
If t.Status = TaskStatus.RanToCompletion Then
Console.WriteLine("Good {0}!",
antecedent.Result)
Console.WriteLine("And how are you this fine {0}?",
antecedent.Result)
Else If t.Status = TaskStatus.Faulted Then
Console.WriteLine(t.Exception.GetBaseException().Message)
End If
End Sub)
End Sub
End Module
' The example displays output like the following:
' Good afternoon!
' And how are you this fine afternoon?
Fortsetzung abbrechen
Die Task.Status -Eigenschaft einer Fortsetzung wird in den folgenden Situationen auf TaskStatus.Canceled festgelegt:
Es löst eine OperationCanceledException Ausnahme als Reaktion auf eine Abbruchanforderung aus. Wenn die Ausnahme das gleiche Token enthält, das auch an die Fortsetzung übergeben wurde, wird dies wie bei allen Aufgaben als Bestätigung des kooperativen Abbruchs interpretiert.
Der Fortsetzung wird ein System.Threading.CancellationToken übergeben, dessen IsCancellationRequested -Eigenschaft auf
true
festgelegt ist. In diesem Fall startet die Fortsetzung nicht, und es wechselt in den TaskStatus.Canceled Zustand.Die Fortsetzung wird nie ausgeführt, da die durch das TaskContinuationOptions Argument festgelegte Bedingung nicht erfüllt wurde. Wenn beispielsweise ein Vorgänger in einen TaskStatus.Faulted-Zustand übergeht, wird seine Fortsetzung, an die die TaskContinuationOptions.NotOnFaulted-Option übergeben wurde, nicht ausgeführt, sondern geht in den Canceled-Zustand über.
Wenn eine Aufgabe und ihre Fortsetzung zwei Teile desselben logischen Vorgangs darstellen, können Sie dasselbe Abbruchtoken an beide Aufgaben übergeben, wie im folgenden Beispiel gezeigt. Es besteht aus einem Vorgänger, der eine Liste mit ganzen Zahlen erstellt, die durch 33 teilbar sind und übergibt sie an die Fortsetzung. Die Fortsetzung zeigt ihrerseits die Liste an. Sowohl der Vorgänger als auch die Fortsetzung werden regelmäßig in zufälligen Intervallen angehalten. Darüber hinaus wird ein System.Threading.Timer Objekt verwendet, um die Elapsed
Methode nach einem Fünf-Sekunden-Timeoutintervall auszuführen. In diesem Beispiel wird die CancellationTokenSource.Cancel Methode aufgerufen, wodurch die derzeit ausgeführte Aufgabe die CancellationToken.ThrowIfCancellationRequested Methode aufruft. Ob die CancellationTokenSource.Cancel Methode aufgerufen wird, wenn die Vor- oder Fortsetzung ausgeführt wird, hängt von der Dauer der zufällig generierten Pausen ab. Wenn der vorausgehende Vorgang storniert wird, wird der Folgeprozess nicht gestartet. Wird der Vorgänger nicht abgebrochen, kann das Token trotzdem zum Abbrechen der Fortsetzung verwendet werden.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public class CancellationExample
{
static readonly Random s_random = new Random((int)DateTime.Now.Ticks);
public static async Task Main()
{
using var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
var timer = new Timer(Elapsed, cts, 5000, Timeout.Infinite);
var task = Task.Run(
async () =>
{
var product33 = new List<int>();
for (int index = 1; index < short.MaxValue; index++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("\nCancellation requested in antecedent...\n");
token.ThrowIfCancellationRequested();
}
if (index % 2000 == 0)
{
int delay = s_random.Next(16, 501);
await Task.Delay(delay);
}
if (index % 33 == 0)
{
product33.Add(index);
}
}
return product33.ToArray();
}, token);
Task<double> continuation = task.ContinueWith(
async antecedent =>
{
Console.WriteLine("Multiples of 33:\n");
int[] array = antecedent.Result;
for (int index = 0; index < array.Length; index++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("\nCancellation requested in continuation...\n");
token.ThrowIfCancellationRequested();
}
if (index % 100 == 0)
{
int delay = s_random.Next(16, 251);
await Task.Delay(delay);
}
Console.Write($"{array[index]:N0}{(index != array.Length - 1 ? ", " : "")}");
if (Console.CursorLeft >= 74)
{
Console.WriteLine();
}
}
Console.WriteLine();
return array.Average();
}, token).Unwrap();
try
{
await task;
double result = await continuation;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine($"\nAntecedent Status: {task.Status}");
Console.WriteLine($"Continuation Status: {continuation.Status}");
}
static void Elapsed(object? state)
{
if (state is CancellationTokenSource cts)
{
cts.Cancel();
Console.WriteLine("\nCancellation request issued...\n");
}
}
}
// The example displays the similar output:
// Multiples of 33:
//
// 33, 66, 99, 132, 165, 198, 231, 264, 297, 330, 363, 396, 429, 462, 495, 528,
// 561, 594, 627, 660, 693, 726, 759, 792, 825, 858, 891, 924, 957, 990, 1,023,
// 1,056, 1,089, 1,122, 1,155, 1,188, 1,221, 1,254, 1,287, 1,320, 1,353, 1,386,
// 1,419, 1,452, 1,485, 1,518, 1,551, 1,584, 1,617, 1,650, 1,683, 1,716, 1,749,
// 1,782, 1,815, 1,848, 1,881, 1,914, 1,947, 1,980, 2,013, 2,046, 2,079, 2,112,
// 2,145, 2,178, 2,211, 2,244, 2,277, 2,310, 2,343, 2,376, 2,409, 2,442, 2,475,
// 2,508, 2,541, 2,574, 2,607, 2,640, 2,673, 2,706, 2,739, 2,772, 2,805, 2,838,
// 2,871, 2,904, 2,937, 2,970, 3,003, 3,036, 3,069, 3,102, 3,135, 3,168, 3,201,
// 3,234, 3,267, 3,300, 3,333, 3,366, 3,399, 3,432, 3,465, 3,498, 3,531, 3,564,
// 3,597, 3,630, 3,663, 3,696, 3,729, 3,762, 3,795, 3,828, 3,861, 3,894, 3,927,
// 3,960, 3,993, 4,026, 4,059, 4,092, 4,125, 4,158, 4,191, 4,224, 4,257, 4,290,
// 4,323, 4,356, 4,389, 4,422, 4,455, 4,488, 4,521, 4,554, 4,587, 4,620, 4,653,
// 4,686, 4,719, 4,752, 4,785, 4,818, 4,851, 4,884, 4,917, 4,950, 4,983, 5,016,
// 5,049, 5,082, 5,115, 5,148, 5,181, 5,214, 5,247, 5,280, 5,313, 5,346, 5,379,
// 5,412, 5,445, 5,478, 5,511, 5,544, 5,577, 5,610, 5,643, 5,676, 5,709, 5,742,
// Cancellation request issued...
//
// 5,775,
// Cancellation requested in continuation...
//
// The operation was canceled.
//
// Antecedent Status: RanToCompletion
// Continuation Status: Canceled
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim rnd As New Random()
Dim lockObj As New Object()
Dim cts As New CancellationTokenSource()
Dim token As CancellationToken = cts.Token
Dim timer As New Timer(AddressOf Elapsed, cts, 5000, Timeout.Infinite)
Dim t = Task.Run(Function()
Dim product33 As New List(Of Integer)()
For ctr As Integer = 1 To Int16.MaxValue
' Check for cancellation.
If token.IsCancellationRequested Then
Console.WriteLine("\nCancellation requested in antecedent...\n")
token.ThrowIfCancellationRequested()
End If
' Introduce a delay.
If ctr Mod 2000 = 0 Then
Dim delay As Integer
SyncLock lockObj
delay = rnd.Next(16, 501)
End SyncLock
Thread.Sleep(delay)
End If
' Determine if this is a multiple of 33.
If ctr Mod 33 = 0 Then product33.Add(ctr)
Next
Return product33.ToArray()
End Function, token)
Dim continuation = t.ContinueWith(Sub(antecedent)
Console.WriteLine("Multiples of 33:" + vbCrLf)
Dim arr = antecedent.Result
For ctr As Integer = 0 To arr.Length - 1
If token.IsCancellationRequested Then
Console.WriteLine("{0}Cancellation requested in continuation...{0}",
vbCrLf)
token.ThrowIfCancellationRequested()
End If
If ctr Mod 100 = 0 Then
Dim delay As Integer
SyncLock lockObj
delay = rnd.Next(16, 251)
End SyncLock
Thread.Sleep(delay)
End If
Console.Write("{0:N0}{1}", arr(ctr),
If(ctr <> arr.Length - 1, ", ", ""))
If Console.CursorLeft >= 74 Then Console.WriteLine()
Next
Console.WriteLine()
End Sub, token)
Try
continuation.Wait()
Catch e As AggregateException
For Each ie In e.InnerExceptions
Console.WriteLine("{0}: {1}", ie.GetType().Name,
ie.Message)
Next
Finally
cts.Dispose()
End Try
Console.WriteLine(vbCrLf + "Antecedent Status: {0}", t.Status)
Console.WriteLine("Continuation Status: {0}", continuation.Status)
End Sub
Private Sub Elapsed(state As Object)
Dim cts As CancellationTokenSource = TryCast(state, CancellationTokenSource)
If cts Is Nothing Then return
cts.Cancel()
Console.WriteLine("{0}Cancellation request issued...{0}", vbCrLf)
End Sub
End Module
' The example displays output like the following:
' Multiples of 33:
'
' 33, 66, 99, 132, 165, 198, 231, 264, 297, 330, 363, 396, 429, 462, 495, 528,
' 561, 594, 627, 660, 693, 726, 759, 792, 825, 858, 891, 924, 957, 990, 1,023,
' 1,056, 1,089, 1,122, 1,155, 1,188, 1,221, 1,254, 1,287, 1,320, 1,353, 1,386,
' 1,419, 1,452, 1,485, 1,518, 1,551, 1,584, 1,617, 1,650, 1,683, 1,716, 1,749,
' 1,782, 1,815, 1,848, 1,881, 1,914, 1,947, 1,980, 2,013, 2,046, 2,079, 2,112,
' 2,145, 2,178, 2,211, 2,244, 2,277, 2,310, 2,343, 2,376, 2,409, 2,442, 2,475,
' 2,508, 2,541, 2,574, 2,607, 2,640, 2,673, 2,706, 2,739, 2,772, 2,805, 2,838,
' 2,871, 2,904, 2,937, 2,970, 3,003, 3,036, 3,069, 3,102, 3,135, 3,168, 3,201,
' 3,234, 3,267, 3,300, 3,333, 3,366, 3,399, 3,432, 3,465, 3,498, 3,531, 3,564,
' 3,597, 3,630, 3,663, 3,696, 3,729, 3,762, 3,795, 3,828, 3,861, 3,894, 3,927,
' 3,960, 3,993, 4,026, 4,059, 4,092, 4,125, 4,158, 4,191, 4,224, 4,257, 4,290,
' 4,323, 4,356, 4,389, 4,422, 4,455, 4,488, 4,521, 4,554, 4,587, 4,620, 4,653,
' 4,686, 4,719, 4,752, 4,785, 4,818, 4,851, 4,884, 4,917, 4,950, 4,983, 5,016,
' 5,049, 5,082, 5,115, 5,148, 5,181, 5,214, 5,247, 5,280, 5,313, 5,346, 5,379,
' 5,412, 5,445, 5,478, 5,511, 5,544, 5,577, 5,610, 5,643, 5,676, 5,709, 5,742,
' 5,775, 5,808, 5,841, 5,874, 5,907, 5,940, 5,973, 6,006, 6,039, 6,072, 6,105,
' 6,138, 6,171, 6,204, 6,237, 6,270, 6,303, 6,336, 6,369, 6,402, 6,435, 6,468,
' 6,501, 6,534, 6,567, 6,600, 6,633, 6,666, 6,699, 6,732, 6,765, 6,798, 6,831,
' 6,864, 6,897, 6,930, 6,963, 6,996, 7,029, 7,062, 7,095, 7,128, 7,161, 7,194,
' 7,227, 7,260, 7,293, 7,326, 7,359, 7,392, 7,425, 7,458, 7,491, 7,524, 7,557,
' 7,590, 7,623, 7,656, 7,689, 7,722, 7,755, 7,788, 7,821, 7,854, 7,887, 7,920,
' 7,953, 7,986, 8,019, 8,052, 8,085, 8,118, 8,151, 8,184, 8,217, 8,250, 8,283,
' 8,316, 8,349, 8,382, 8,415, 8,448, 8,481, 8,514, 8,547, 8,580, 8,613, 8,646,
' 8,679, 8,712, 8,745, 8,778, 8,811, 8,844, 8,877, 8,910, 8,943, 8,976, 9,009,
' 9,042, 9,075, 9,108, 9,141, 9,174, 9,207, 9,240, 9,273, 9,306, 9,339, 9,372,
' 9,405, 9,438, 9,471, 9,504, 9,537, 9,570, 9,603, 9,636, 9,669, 9,702, 9,735,
' 9,768, 9,801, 9,834, 9,867, 9,900, 9,933, 9,966, 9,999, 10,032, 10,065, 10,098,
' 10,131, 10,164, 10,197, 10,230, 10,263, 10,296, 10,329, 10,362, 10,395, 10,428,
' 10,461, 10,494, 10,527, 10,560, 10,593, 10,626, 10,659, 10,692, 10,725, 10,758,
' 10,791, 10,824, 10,857, 10,890, 10,923, 10,956, 10,989, 11,022, 11,055, 11,088,
' 11,121, 11,154, 11,187, 11,220, 11,253, 11,286, 11,319, 11,352, 11,385, 11,418,
' 11,451, 11,484, 11,517, 11,550, 11,583, 11,616, 11,649, 11,682, 11,715, 11,748,
' 11,781, 11,814, 11,847, 11,880, 11,913, 11,946, 11,979, 12,012, 12,045, 12,078,
' 12,111, 12,144, 12,177, 12,210, 12,243, 12,276, 12,309, 12,342, 12,375, 12,408,
' 12,441, 12,474, 12,507, 12,540, 12,573, 12,606, 12,639, 12,672, 12,705, 12,738,
' 12,771, 12,804, 12,837, 12,870, 12,903, 12,936, 12,969, 13,002, 13,035, 13,068,
' 13,101, 13,134, 13,167, 13,200, 13,233, 13,266,
' Cancellation requested in continuation...
'
'
' Cancellation request issued...
'
' TaskCanceledException: A task was canceled.
'
' Antecedent Status: RanToCompletion
' Continuation Status: Canceled
Sie können auch verhindern, dass eine Fortsetzung ausgeführt wird, wenn deren Vorgänger abgebrochen wird, ohne der Fortsetzung ein Abbruch-Token bereitzustellen. Geben Sie das Token an, indem Sie die TaskContinuationOptions.NotOnCanceled Option angeben, wenn Sie die Fortsetzung erstellen, wie im folgenden Beispiel gezeigt:
using System;
using System.Threading;
using System.Threading.Tasks;
public class CancellationTwoExample
{
public static async Task Main()
{
using var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
cts.Cancel();
var task = Task.FromCanceled(token);
Task continuation =
task.ContinueWith(
antecedent => Console.WriteLine("The continuation is running."),
TaskContinuationOptions.NotOnCanceled);
try
{
await task;
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
Console.WriteLine();
}
Console.WriteLine($"Task {task.Id}: {task.Status:G}");
Console.WriteLine($"Task {continuation.Id}: {continuation.Status:G}");
}
}
// The example displays the similar output:
// TaskCanceledException: A task was canceled.
//
// Task 1: Canceled
// Task 2: Canceled
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim cts As New CancellationTokenSource()
Dim token As CancellationToken = cts.Token
cts.Cancel()
Dim t As Task = Task.FromCanceled(token)
Dim continuation As Task = t.ContinueWith(Sub(antecedent)
Console.WriteLine("The continuation is running.")
End Sub, TaskContinuationOptions.NotOnCanceled)
Try
t.Wait()
Catch e As AggregateException
For Each ie In e.InnerExceptions
Console.WriteLine("{0}: {1}", ie.GetType().Name, ie.Message)
Next
Console.WriteLine()
Finally
cts.Dispose()
End Try
Console.WriteLine("Task {0}: {1:G}", t.Id, t.Status)
Console.WriteLine("Task {0}: {1:G}", continuation.Id,
continuation.Status)
End Sub
End Module
' The example displays the following output:
' TaskCanceledException: A task was canceled.
'
' Task 1: Canceled
' Task 2: Canceled
Nachdem eine Fortsetzung in den Zustand Canceled übergeht, kann sie sich auf nachfolgende Fortsetzungen auswirken, abhängig davon, welche TaskContinuationOptions für diese Fortsetzungen angegeben wurden.
Gelöschte Fortsetzungen werden nicht gestartet.
Fortsetzungen und untergeordnete Tasks
Eine Fortsetzung wird erst ausgeführt, wenn der Vorgänger und alle zugehörigen untergeordneten Aufgaben abgeschlossen wurden. Eine Fortsetzung wartet nicht, bis getrennte untergeordnete Aufgaben beendet wurden. Die beiden folgenden Beispiele veranschaulichen untergeordnete Aufgaben, die an einen Vorgänger, der eine Fortsetzung erstellt, angefügt und von ihm getrennt werden. Im folgenden Beispiel wird die Fortsetzung nur ausgeführt, wenn alle untergeordneten Aufgaben abgeschlossen wurden, und mehrfache Ausführungen des Beispiels liefern stets die gleiche Ausgabe. Im Beispiel wird der Vorgänger gestartet, indem die TaskFactory.StartNew Methode aufgerufen wird, da standardmäßig die Task.Run Methode eine übergeordnete Aufgabe erstellt, deren Standardoption für die Aufgabenerstellung TaskCreationOptions.DenyChildAttach lautet.
using System;
using System.Threading.Tasks;
public class AttachedExample
{
public static async Task Main()
{
await Task.Factory
.StartNew(
() =>
{
Console.WriteLine($"Running antecedent task {Task.CurrentId}...");
Console.WriteLine("Launching attached child tasks...");
for (int ctr = 1; ctr <= 5; ctr++)
{
int index = ctr;
Task.Factory.StartNew(async value =>
{
Console.WriteLine($" Attached child task #{value} running");
await Task.Delay(1000);
}, index, TaskCreationOptions.AttachedToParent);
}
Console.WriteLine("Finished launching attached child tasks...");
}).ContinueWith(
antecedent =>
Console.WriteLine($"Executing continuation of Task {antecedent.Id}"));
}
}
// The example displays the similar output:
// Running antecedent task 1...
// Launching attached child tasks...
// Finished launching attached child tasks...
// Attached child task #1 running
// Attached child task #5 running
// Attached child task #3 running
// Attached child task #2 running
// Attached child task #4 running
// Executing continuation of Task 1
Imports System.Threading
Imports System.Threading.Tasks
Public Module Example
Public Sub Main()
Dim t = Task.Factory.StartNew(Sub()
Console.WriteLine("Running antecedent task {0}...",
Task.CurrentId)
Console.WriteLine("Launching attached child tasks...")
For ctr As Integer = 1 To 5
Dim index As Integer = ctr
Task.Factory.StartNew(Sub(value)
Console.WriteLine(" Attached child task #{0} running",
value)
Thread.Sleep(1000)
End Sub, index, TaskCreationOptions.AttachedToParent)
Next
Console.WriteLine("Finished launching attached child tasks...")
End Sub)
Dim continuation = t.ContinueWith(Sub(antecedent)
Console.WriteLine("Executing continuation of Task {0}",
antecedent.Id)
End Sub)
continuation.Wait()
End Sub
End Module
' The example displays the following output:
' Running antecedent task 1...
' Launching attached child tasks...
' Finished launching attached child tasks...
' Attached child task #5 running
' Attached child task #1 running
' Attached child task #2 running
' Attached child task #3 running
' Attached child task #4 running
' Executing continuation of Task 1
Wenn jedoch untergeordnete Aufgaben vom Vorgänger getrennt werden, wird die Fortsetzung ausgeführt, sobald der Vorgänger abgeschlossen wurde, unabhängig vom Zustand der untergeordneten Aufgaben. Daher können mehrfache Ausführungen des folgenden Beispiels unterschiedliche Ausgaben generieren, die davon abhängen, wie der Taskplaner die einzelnen untergeordneten Aufgaben verarbeitet hat:
using System;
using System.Threading.Tasks;
public class DetachedExample
{
public static async Task Main()
{
Task task =
Task.Factory.StartNew(
() =>
{
Console.WriteLine($"Running antecedent task {Task.CurrentId}...");
Console.WriteLine("Launching attached child tasks...");
for (int ctr = 1; ctr <= 5; ctr++)
{
int index = ctr;
Task.Factory.StartNew(
async value =>
{
Console.WriteLine($" Attached child task #{value} running");
await Task.Delay(1000);
}, index);
}
Console.WriteLine("Finished launching detached child tasks...");
}, TaskCreationOptions.DenyChildAttach);
Task continuation =
task.ContinueWith(
antecedent =>
Console.WriteLine($"Executing continuation of Task {antecedent.Id}"));
await continuation;
Console.ReadLine();
}
}
// The example displays the similar output:
// Running antecedent task 1...
// Launching attached child tasks...
// Finished launching detached child tasks...
// Executing continuation of Task 1
// Attached child task #1 running
// Attached child task #5 running
// Attached child task #2 running
// Attached child task #3 running
// Attached child task #4 running
Imports System.Threading
Imports System.Threading.Tasks
Public Module Example
Public Sub Main()
Dim t = Task.Factory.StartNew(Sub()
Console.WriteLine("Running antecedent task {0}...",
Task.CurrentId)
Console.WriteLine("Launching attached child tasks...")
For ctr As Integer = 1 To 5
Dim index As Integer = ctr
Task.Factory.StartNew(Sub(value)
Console.WriteLine(" Attached child task #{0} running",
value)
Thread.Sleep(1000)
End Sub, index)
Next
Console.WriteLine("Finished launching detached child tasks...")
End Sub, TaskCreationOptions.DenyChildAttach)
Dim continuation = t.ContinueWith(Sub(antecedent)
Console.WriteLine("Executing continuation of Task {0}",
antecedent.Id)
End Sub)
continuation.Wait()
End Sub
End Module
' The example displays output like the following:
' Running antecedent task 1...
' Launching attached child tasks...
' Finished launching detached child tasks...
' Attached child task #1 running
' Attached child task #2 running
' Attached child task #5 running
' Attached child task #3 running
' Executing continuation of Task 1
' Attached child task #4 running
Der Endstatus der Vorgängeraufgabe hängt vom Endstatus aller zugehörigen untergeordneten Aufgaben ab. Der Status getrennter untergeordneter Aufgaben wirkt sich nicht auf das übergeordnete Element aus. Weitere Informationen finden Sie unter Angefügte und getrennte untergeordnete Aufgaben.
Zuordnen eines Zustands zu Fortsetzungen
Sie können einen beliebigen Zustand einer Aufgabenfortsetzung zuordnen. Die ContinueWith Methode stellt überladene Versionen bereit, die jeweils einen Object Wert annehmen, der den Status der Fortsetzung darstellt. Sie können später mithilfe der Task.AsyncState Eigenschaft auf dieses Statusobjekt zugreifen. Dieses Statusobjekt ist null
, wenn Sie keinen Wert angeben.
Der Fortsetzungszustand ist bei der Verwendung der TPL nützlich, wenn Sie vorhandenen Code konvertieren, der das Asynchrone Programmiermodell (APM) verwendet. Im APM können Sie den Objektzustand in der BeginMethode bereitstellen, und später können Sie die IAsyncResult.AsyncState-Eigenschaft verwenden, um auf diesen Zustand zuzugreifen. Um diesen Zustand beizubehalten, wenn Sie einen Code konvertieren, der die APM für die Verwendung der TPL verwendet, verwenden Sie die ContinueWith Methode.
Der Fortsetzungszustand kann auch hilfreich sein, wenn Sie mit Task Objekten im Visual Studio-Debugger arbeiten. Beispielsweise zeigt die Spalte "Vorgang" im Fenster "Parallele Vorgänge" die Zeichenfolgendarstellung des Zustandsobjekts für jeden Vorgang an. Weitere Informationen zum Fenster " Parallele Vorgänge " finden Sie unter "Verwenden des Aufgabenfensters".
Das folgende Beispiel zeigt, wie der Fortsetzungszustand verwendet wird. Sie erstellt eine Kette von Fortsetzungsaufgaben. Jeder Vorgang stellt die aktuelle Uhrzeit, ein DateTime Objekt, für den state
Parameter der ContinueWith Methode bereit. Jedes DateTime Objekt stellt den Zeitpunkt dar, zu dem die Fortsetzungsaufgabe erstellt wird. Jeder Vorgang erzeugt als Ergebnis ein zweites DateTime Objekt, das die Uhrzeit darstellt, zu der der Vorgang beendet wird. Nach Abschluss aller Vorgänge zeigt dieses Beispiel die Erstellungszeit und die Uhrzeit an, zu der jeder Fortsetzungsvorgang abgeschlossen ist.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
class ContinuationStateExample
{
static DateTime DoWork()
{
Thread.Sleep(2000);
return DateTime.Now;
}
static async Task Main()
{
Task<DateTime> task = Task.Run(() => DoWork());
var continuations = new List<Task<DateTime>>();
for (int i = 0; i < 5; i++)
{
task = task.ContinueWith((antecedent, _) => DoWork(), DateTime.Now);
continuations.Add(task);
}
await task;
foreach (Task<DateTime> continuation in continuations)
{
DateTime start = (DateTime)continuation.AsyncState!;
DateTime end = continuation.Result;
Console.WriteLine($"Task was created at {start.TimeOfDay} and finished at {end.TimeOfDay}.");
}
Console.ReadLine();
}
}
// The example displays the similar output:
// Task was created at 10:56:21.1561762 and finished at 10:56:25.1672062.
// Task was created at 10:56:21.1610677 and finished at 10:56:27.1707646.
// Task was created at 10:56:21.1610677 and finished at 10:56:29.1743230.
// Task was created at 10:56:21.1610677 and finished at 10:56:31.1779883.
// Task was created at 10:56:21.1610677 and finished at 10:56:33.1837083.
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
' Demonstrates how to associate state with task continuations.
Public Module ContinuationState
' Simulates a lengthy operation and returns the time at which
' the operation completed.
Public Function DoWork() As Date
' Simulate work by suspending the current thread
' for two seconds.
Thread.Sleep(2000)
' Return the current time.
Return Date.Now
End Function
Public Sub Main()
' Start a root task that performs work.
Dim t As Task(Of Date) = Task(Of Date).Run(Function() DoWork())
' Create a chain of continuation tasks, where each task is
' followed by another task that performs work.
Dim continuations As New List(Of Task(Of DateTime))()
For i As Integer = 0 To 4
' Provide the current time as the state of the continuation.
t = t.ContinueWith(Function(antecedent, state) DoWork(), DateTime.Now)
continuations.Add(t)
Next
' Wait for the last task in the chain to complete.
t.Wait()
' Display the creation time of each continuation (the state object)
' and the completion time (the result of that task) to the console.
For Each continuation In continuations
Dim start As DateTime = CDate(continuation.AsyncState)
Dim [end] As DateTime = continuation.Result
Console.WriteLine("Task was created at {0} and finished at {1}.",
start.TimeOfDay, [end].TimeOfDay)
Next
End Sub
End Module
' The example displays output like the following:
' Task was created at 10:56:21.1561762 and finished at 10:56:25.1672062.
' Task was created at 10:56:21.1610677 and finished at 10:56:27.1707646.
' Task was created at 10:56:21.1610677 and finished at 10:56:29.1743230.
' Task was created at 10:56:21.1610677 and finished at 10:56:31.1779883.
' Task was created at 10:56:21.1610677 and finished at 10:56:33.1837083.
Fortsetzungen, die Aufgabentypen zurückgeben
Manchmal ist es ggf. erforderlich, eine Fortsetzung zu verketten, die einen Task-Typ zurückgibt. Diese Vorgänge werden als geschachtelte Vorgänge bezeichnet. Wenn eine übergeordnete Aufgabe Task<TResult>.ContinueWith aufruft und eine Fortsetzungsfunktion (continuationFunction
) angibt, die eine Aufgabe zurückgibt, können Sie Unwrap aufrufen, um eine Proxyaufgabe zu erstellen, die den asynchronen Vorgang von <Task<Task<T>>>
oder Task(Of Task(Of T))
(Visual Basic) darstellt.
Das folgende Beispiel zeigt, wie Fortsetzungen verwendet werden, die zusätzliche Aufgabenrückgabefunktionen umschließen. Die Umschließung jeder Fortsetzung kann aufgehoben werden, um den inneren Task verfügbar zu machen, der umschlossen war.
using System;
using System.Threading;
using System.Threading.Tasks;
public class UnwrapExample
{
public static async Task Main()
{
Task<int> taskOne = RemoteIncrement(0);
Console.WriteLine("Started RemoteIncrement(0)");
Task<int> taskTwo = RemoteIncrement(4)
.ContinueWith(t => RemoteIncrement(t.Result))
.Unwrap().ContinueWith(t => RemoteIncrement(t.Result))
.Unwrap().ContinueWith(t => RemoteIncrement(t.Result))
.Unwrap();
Console.WriteLine("Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)");
try
{
await taskOne;
Console.WriteLine("Finished RemoteIncrement(0)");
await taskTwo;
Console.WriteLine("Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)");
}
catch (Exception e)
{
Console.WriteLine($"A task has thrown the following (unexpected) exception:\n{e}");
}
}
static Task<int> RemoteIncrement(int number) =>
Task<int>.Factory.StartNew(
obj =>
{
Thread.Sleep(1000);
int x = (int)(obj!);
Console.WriteLine("Thread={0}, Next={1}", Thread.CurrentThread.ManagedThreadId, ++x);
return x;
},
number);
}
// The example displays the similar output:
// Started RemoteIncrement(0)
// Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
// Thread=4, Next=1
// Finished RemoteIncrement(0)
// Thread=5, Next=5
// Thread=6, Next=6
// Thread=6, Next=7
// Thread=6, Next=8
// Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
Imports System.Threading
Module UnwrapExample
Sub Main()
Dim taskOne As Task(Of Integer) = RemoteIncrement(0)
Console.WriteLine("Started RemoteIncrement(0)")
Dim taskTwo As Task(Of Integer) = RemoteIncrement(4).
ContinueWith(Function(t) RemoteIncrement(t.Result)).
Unwrap().ContinueWith(Function(t) RemoteIncrement(t.Result)).
Unwrap().ContinueWith(Function(t) RemoteIncrement(t.Result)).
Unwrap()
Console.WriteLine("Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)")
Try
taskOne.Wait()
Console.WriteLine("Finished RemoteIncrement(0)")
taskTwo.Wait()
Console.WriteLine("Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)")
Catch e As AggregateException
Console.WriteLine($"A task has thrown the following (unexpected) exception:{vbLf}{e}")
End Try
End Sub
Function RemoteIncrement(ByVal number As Integer) As Task(Of Integer)
Return Task(Of Integer).Factory.StartNew(
Function(obj)
Thread.Sleep(1000)
Dim x As Integer = CInt(obj)
Console.WriteLine("Thread={0}, Next={1}", Thread.CurrentThread.ManagedThreadId, Interlocked.Increment(x))
Return x
End Function, number)
End Function
End Module
' The example displays the similar output:
' Started RemoteIncrement(0)
' Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
' Thread=4, Next=1
' Finished RemoteIncrement(0)
' Thread=5, Next=5
' Thread=6, Next=6
' Thread=6, Next=7
' Thread=6, Next=8
' Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
Weitere Informationen zur Verwendung von Unwrap finden Sie unter So geht's: Eine geschachtelte Aufgabe entpacken.
Behandeln von durch Fortsetzungen ausgelösten Ausnahmen
Eine Antezedens-Fortsetzungs-Beziehung ist keine Eltern-Kind-Beziehung. Durch Fortsetzungen ausgelöste Ausnahmen werden nicht an den Vorgänger weitergegeben. Behandeln Sie daher Ausnahmen, die von Fortsetzungen ausgelöst werden, genau wie in jeder anderen Aufgabe, und zwar wie folgt:
- Sie können die Wait, WaitAll oder WaitAny Methode oder ihr generisches Gegenstück verwenden, um auf die Fortsetzung zu warten. Sie können in der gleichen
try
-Anweisung auf einen Vorgänger und seine Fortsetzungen warten, wie im folgenden Beispiel gezeigt:
using System;
using System.Threading.Tasks;
public class ExceptionExample
{
public static async Task Main()
{
Task<int> task = Task.Run(
() =>
{
Console.WriteLine($"Executing task {Task.CurrentId}");
return 54;
});
var continuation = task.ContinueWith(
antecedent =>
{
Console.WriteLine($"Executing continuation task {Task.CurrentId}");
Console.WriteLine($"Value from antecedent: {antecedent.Result}");
throw new InvalidOperationException();
});
try
{
await task;
await continuation;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
// The example displays the similar output:
// Executing task 1
// Executing continuation task 2
// Value from antecedent: 54
// Operation is not valid due to the current state of the object.
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim task1 = Task(Of Integer).Run(Function()
Console.WriteLine("Executing task {0}",
Task.CurrentId)
Return 54
End Function)
Dim continuation = task1.ContinueWith(Sub(antecedent)
Console.WriteLine("Executing continuation task {0}",
Task.CurrentId)
Console.WriteLine("Value from antecedent: {0}",
antecedent.Result)
Throw New InvalidOperationException()
End Sub)
Try
task1.Wait()
continuation.Wait()
Catch ae As AggregateException
For Each ex In ae.InnerExceptions
Console.WriteLine(ex.Message)
Next
End Try
End Sub
End Module
' The example displays the following output:
' Executing task 1
' Executing continuation task 2
' Value from antecedent: 54
' Operation is not valid due to the current state of the object.
- Sie können eine zweite Fortsetzung verwenden, um die Exception -Eigenschaft der ersten Fortsetzung zu beachten. Im folgenden Beispiel versucht eine Aufgabe, aus einer nicht vorhandenen Datei zu lesen. Die Fortsetzung zeigt dann Informationen zur Ausnahme in der vorhergehenden Aufgabe an.
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
public class ExceptionTwoExample
{
public static async Task Main()
{
var task = Task.Run(
() =>
{
string fileText = File.ReadAllText(@"C:\NonexistentFile.txt");
return fileText;
});
Task continuation = task.ContinueWith(
antecedent =>
{
var fileNotFound =
antecedent.Exception
?.InnerExceptions
?.FirstOrDefault(e => e is FileNotFoundException) as FileNotFoundException;
if (fileNotFound != null)
{
Console.WriteLine(fileNotFound.Message);
}
}, TaskContinuationOptions.OnlyOnFaulted);
await continuation;
Console.ReadLine();
}
}
// The example displays the following output:
// Could not find file 'C:\NonexistentFile.txt'.
Imports System.IO
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim t = Task.Run(Function()
Dim s As String = File.ReadAllText("C:\NonexistentFile.txt")
Return s
End Function)
Dim c = t.ContinueWith(Sub(antecedent)
' Get the antecedent's exception information.
For Each ex In antecedent.Exception.InnerExceptions
If TypeOf ex Is FileNotFoundException
Console.WriteLine(ex.Message)
End If
Next
End Sub, TaskContinuationOptions.OnlyOnFaulted)
c.Wait()
End Sub
End Module
' The example displays the following output:
' Could not find file 'C:\NonexistentFile.txt'.
Da es mit der TaskContinuationOptions.OnlyOnFaulted Option ausgeführt wurde, wird die Fortsetzung nur dann ausgeführt, wenn im Vorgänger eine Ausnahme auftritt. Daher kann davon ausgegangen werden, dass die Eigenschaft des vorherigen Exception Objekts nicht null
ist. Wenn die Fortsetzung unabhängig davon ausgeführt wird, ob im Vorgänger eine Ausnahme ausgelöst wird, muss überprüft werden, ob die Exception-Eigenschaft des Vorgängers nicht null
ist, bevor versucht wird, die Ausnahme zu behandeln, wie das folgende Codefragment zeigt.
var fileNotFound =
antecedent.Exception
?.InnerExceptions
?.FirstOrDefault(e => e is FileNotFoundException) as FileNotFoundException;
if (fileNotFound != null)
{
Console.WriteLine(fileNotFound.Message);
}
' Determine whether an exception occurred.
If antecedent.Exception IsNot Nothing Then
' Get the antecedent's exception information.
For Each ex In antecedent.Exception.InnerExceptions
If TypeOf ex Is FileNotFoundException
Console.WriteLine(ex.Message)
End If
Next
End If
Weitere Informationen finden Sie unter "Ausnahmebehandlung".
- Wenn die Fortsetzung eine angefügte untergeordnete Aufgabe ist, die mit der TaskContinuationOptions.AttachedToParent -Option erstellt wurde, werden die zugehörigen Ausnahmen vom übergeordneten Element an den aufrufenden Thread zurückgegeben, wie dies auch bei allen anderen angefügten untergeordneten Elementen der Fall ist. Weitere Informationen finden Sie unter Angefügte und getrennte untergeordnete Aufgaben.