Verketten von Aufgaben mithilfe von FortsetzungsaufgabenChaining Tasks by Using Continuation Tasks

Bei der asynchronen Programmierung werden nach Abschluss eines asynchronen Vorgangs häufig ein zweiter Vorgang aufgerufen und Daten an diesen weitergegeben.In asynchronous programming, it is common for one asynchronous operation, on completion, to invoke a second operation and pass data to it. In der Vergangenheit wurden für diese Fortsetzungen vor allem Rückrufmethoden genutzt.Traditionally, continuations have been done by using callback methods. In der Task Parallel Library wird die gleiche Funktionalität durch Fortsetzungsaufgabenbereitgestellt.In the Task Parallel Library, the same functionality is provided by continuation tasks. Eine Fortsetzungsaufgabe (auch kurz als Fortsetzung bezeichnet) ist eine asynchrone Aufgabe, die von einer anderen Aufgabe, die wiederum als Vorgängerbezeichnet wird, nach deren Beendigung aufgerufen wird.A continuation task (also known just as a continuation) is an asynchronous task that is invoked by another task, which is known as the antecedent, when the antecedent finishes.

Fortsetzungen können relativ einfach eingesetzt werden, sind dabei jedoch sehr leistungsstark und flexibel.Continuations are relatively easy to use, but are nevertheless powerful and flexible. Sie haben unter anderem folgende Möglichkeiten:For example, you can:

  • Übergeben von Daten vom Vorgänger an die FortsetzungPass data from the antecedent to the continuation.

  • Angeben der präzisen Bedingungen, unter denen die Fortsetzung aufgerufen bzw. nicht aufgerufen wirdSpecify the precise conditions under which the continuation will be invoked or not invoked.

  • Abbrechen einer Fortsetzung vor dem Starten oder kooperativ während ihrer AusführungCancel a continuation either before it starts or cooperatively as it is running.

  • Bereitstellen von Hinweisen zur Planung der FortsetzungProvide hints about how the continuation should be scheduled.

  • Aufrufen mehrerer Fortsetzungen durch den gleichen VorgängerInvoke multiple continuations from the same antecedent.

  • Aufrufen einer Fortsetzung, wenn alle Vorgänger abgeschlossen werden oder einer von mehreren Vorgängern abgeschlossen wirdInvoke one continuation when all or any one of multiple antecedents complete.

  • Verketten von Fortsetzungen der Reihe nach auf eine beliebige LängeChain continuations one after another to any arbitrary length.

  • Beheben von durch den Vorgänger ausgelösten Ausnahmen mithilfe einer FortsetzungUse a continuation to handle exceptions thrown by the antecedent.

Über FortsetzungenAbout continuations

Eine Fortsetzung ist eine Aufgabe, die im WaitingForActivation -Zustand erstellt wird.A continuation is a task that is created in the WaitingForActivation state. Sie wird automatisch aktiviert, wenn ihre Vorgängeraufgabe bzw. -aufgaben abgeschlossen wird bzw. werden.It is activated automatically when its antecedent task or tasks complete. Das Aufrufen von Task.Start für eine Fortsetzung in Benutzercode löst eine System.InvalidOperationException -Ausnahme aus.Calling Task.Start on a continuation in user code throws an System.InvalidOperationException exception.

Eine Fortsetzung ist selbst ein Task und blockiert nicht den Thread, in dem sie gestartet wird.A continuation is itself a Task and does not block the thread on which it is started. Rufen Sie die Task.Wait -Methode auf, um den Thread bis zum Abschluss der Fortsetzungsaufgabe zu blockieren.Call the Task.Wait method to block until the continuation task finishes.

Erstellen einer Fortsetzung für einen einzelnen VorgängerCreating a continuation for a single antecedent

Sie erstellen eine Fortsetzung, die durch Aufrufen der Methode Task.ContinueWith ausgeführt wird, wenn ihr Vorgänger abgeschlossen wurde.You create a continuation that executes when its antecedent has completed by calling the Task.ContinueWith method. Im folgenden Beispiel ist das grundlegende Muster dargestellt (aus Gründen der Übersichtlichkeit wird die Ausnahmebehandlung ausgelassen).The following example shows the basic pattern (for clarity, exception handling is omitted). Es führt eine Vorgängeraufgabe taskAaus, der ein DayOfWeek -Objekt zurückgibt, der den Namen des aktuellen Wochentags angibt.It executes an antecedent task, taskA, that returns a DayOfWeek object that indicates the name of the current day of the week. Nach Abschluss des Vorgängers wird der Fortsetzungsaufgabe continuation der Vorgänger übergeben; sie zeigt dann eine Zeichenfolge an, die dessen Ergebnis beinhaltet.When the antecedent completes, the continuation task, continuation, is passed the antecedent and displays a string that includes its result.

Hinweis

Die C#-Beispiele in diesem Artikel verwenden den async-Modifizierer und die Main-Methode.The C# samples in this article make use of the async modifier on the Main method. Dieses Feature ist in C# 7.1 und höher verfügbar.That feature is available in C# 7.1 and later. In älteren Versionen wird beim Kompilieren des Beispielcodes CS5001 generiert.Previous versions generate CS5001 when compiling this sample code. Sie müssen deshalb als Sprachversion C# 7.1 oder höher verwenden.You'll need to set the language version to C# 7.1 or newer. Im Artikel Auswählen der C#-Sprachversion erfahren Sie, wie Sie die Sprachversion konfigurieren können.You can learn how to configure the language version in the article on configure language version.

using System;
using System.Threading.Tasks;

public class Example
{
   public static async Task Main()
   {
      // Execute the antecedent.
      Task<DayOfWeek> taskA = Task.Run( () => DateTime.Today.DayOfWeek );

      // Execute the continuation when the antecedent finishes.
      await taskA.ContinueWith( antecedent => Console.WriteLine("Today is {0}.", antecedent.Result) );
   }
}
// The example displays output like 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ängerCreating a continuation for multiple antecedents

Sie können auch eine Fortsetzung erstellen, die ausgeführt wird, wenn beliebige oder alle Aufgaben einer Gruppe abgeschlossen wurden.You can also create a continuation that will run when any or all of a group of tasks has completed. Zum Ausführen einer Fortsetzung nach dem Abschluss aller Vorgängeraufgaben rufen Sie die statische (Shared in Visual Basic) Task.WhenAll -Methode oder die TaskFactory.ContinueWhenAll -Instanzmethode auf.To execute a continuation when all antecedent tasks have completed, you call the static (Shared in Visual Basic) Task.WhenAll method or the instance TaskFactory.ContinueWhenAll method. Zum Ausführen einer Fortsetzung nach dem Abschluss einer beliebigen Vorgängeraufgabe rufen Sie die statische (Shared in Visual Basic) Task.WhenAny -Methode oder die TaskFactory.ContinueWhenAny -Instanzmethode auf.To execute a continuation when any of the antecedent tasks has completed, you call the static (Shared in Visual Basic) Task.WhenAny method or the instance TaskFactory.ContinueWhenAny method.

Beachten Sie, dass Aufrufe der Task.WhenAll - und Task.WhenAny -Überladungen den aufrufenden Thread nicht blockieren.Note that calls to the Task.WhenAll and Task.WhenAny overloads do not block the calling thread. Allerdings rufen Sie in der Regel alle Methoden außer den Methoden Task.WhenAll(IEnumerable<Task>) und Task.WhenAll(Task[]) auf, um die zurückgegebene Task<TResult>.Result-Eigenschaft abzurufen, die den aufrufenden Thread blockiert.However, you typically call all but the Task.WhenAll(IEnumerable<Task>) and Task.WhenAll(Task[]) methods to retrieve the returned Task<TResult>.Result property, which does block the calling thread.

Im folgenden Beispiel wird die Task.WhenAll(IEnumerable<Task>) -Methode aufgerufen, um eine Fortsetzungsaufgabe zu erstellen, die die Ergebnisse ihrer zehn Vorgängeraufgaben wiedergibt.The following example calls the Task.WhenAll(IEnumerable<Task>) method to create a continuation task that reflects the results of its 10 antecedent tasks. Jede Vorgängeraufgabe errechnet das Quadrat eines Indexwerts aus dem Bereich von 1 bis 10.Each antecedent task squares an index value that ranges from one to 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.If the antecedents complete successfully (their Task.Status property is TaskStatus.RanToCompletion), the Task<TResult>.Result property of the continuation is an array of the Task<TResult>.Result values returned by each antecedent. Im Beispiel werden sie addiert, um die Summe der Quadrate für alle Zahlen zwischen eins und zehn zu berechnen.The example adds them to compute the sum of squares for all numbers between one and 10.

using System.Collections.Generic;
using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      List<Task<int>> tasks = new List<Task<int>>();
      for (int ctr = 1; ctr <= 10; ctr++) {
         int baseValue = ctr;
         tasks.Add(Task.Factory.StartNew( (b) => { int i = (int) b;
                                                   return i * i; }, baseValue));
      }
      var continuation = Task.WhenAll(tasks);

      long sum = 0;
      for (int ctr = 0; ctr <= continuation.Result.Length - 1; ctr++) {
         Console.Write("{0} {1} ", continuation.Result[ctr],
                       ctr == continuation.Result.Length - 1 ? "=" : "+");
         sum += continuation.Result[ctr];
      }
      Console.WriteLine(sum);
   }
}
// The example displays the following 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

FortsetzungsoptionenContinuation options

Wenn Sie eine Fortsetzung einer einzelnen Aufgabe erstellen, können Sie eine ContinueWith -Überladung erstellen, die einen System.Threading.Tasks.TaskContinuationOptions -Enumerationswert annimmt, um die Bedingungen anzugeben, unter denen die Vorgängeraufgabe die Fortsetzung startet.When you create a single-task continuation, you can use a ContinueWith overload that takes a System.Threading.Tasks.TaskContinuationOptions enumeration value to specify the conditions under which the continuation starts. 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.For example, you can specify that the continuation is to run only if the antecedent completes successfully, or only if it completes in a faulted state. Wenn die Bedingung zu dem Zeitpunkt, zu dem der Vorgänger bereit ist, die Fortsetzung aufzurufen, nicht erfüllt ist, geht die Fortsetzung direkt in den Zustand TaskStatus.Canceled über und kann anschließend nicht mehr gestartet werden.If the condition is not true when the antecedent is ready to invoke the continuation, the continuation transitions directly to the TaskStatus.Canceled state and subsequently cannot be started.

Eine Reihe von Fortsetzungsmethoden für mehrere Aufgaben, wie etwa Überladungen der Methode TaskFactory.ContinueWhenAll , beinhalten außerdem einen Parameter System.Threading.Tasks.TaskContinuationOptions .A number of multi-task continuation methods, such as overloads of the TaskFactory.ContinueWhenAll method, also include a System.Threading.Tasks.TaskContinuationOptions parameter. Allerdings ist nur eine Teilmenge aller Elemente der System.Threading.Tasks.TaskContinuationOptions -Enumeration gültig.Only a subset of all System.Threading.Tasks.TaskContinuationOptions enumeration members are valid, however. Sie können System.Threading.Tasks.TaskContinuationOptions -Werte angeben, die Entsprechungen in der System.Threading.Tasks.TaskCreationOptions -Enumeration aufweisen, wie etwa TaskContinuationOptions.AttachedToParent, TaskContinuationOptions.LongRunningund TaskContinuationOptions.PreferFairness.You can specify System.Threading.Tasks.TaskContinuationOptions values that have counterparts in the System.Threading.Tasks.TaskCreationOptions enumeration, such as TaskContinuationOptions.AttachedToParent, TaskContinuationOptions.LongRunning, and TaskContinuationOptions.PreferFairness. Wenn Sie für eine Fortsetzung mit mehreren Aufgaben die NotOn -Option oder die OnlyOn -Option angeben, wird zur Laufzeit eine ArgumentOutOfRangeException -Ausnahme ausgelöst.If you specify any of the NotOn or OnlyOn options with a multi-task continuation, an ArgumentOutOfRangeException exception will be thrown at run time.

Weitere Informationen zu den Optionen für die Fortsetzung von Aufgaben finden Sie im Thema TaskContinuationOptions .For more information on task continuation options, see the TaskContinuationOptions topic.

Übergeben von Daten an eine FortsetzungPassing Data to a Continuation

Die Methode Task.ContinueWith übergibt einen Verweis auf den Vorgänger als Argument an den Benutzerstellvertreter der Fortsetzung.The Task.ContinueWith method passes a reference to the antecedent to the user delegate of the continuation as an argument. Wenn es sich beim dem Vorgänger um ein System.Threading.Tasks.Task<TResult> -Objekt handelt und die Aufgabe bis zum Abschluss ausgeführt wurde, kann die Fortsetzung auf die Task<TResult>.Result -Eigenschaft der Aufgabe zugreifen.If the antecedent is a System.Threading.Tasks.Task<TResult> object, and the task ran until it was completed, then the continuation can access the Task<TResult>.Result property of the task.

Die Task<TResult>.Result -Eigenschaft ist bis zum Abschluss der Aufgabe blockiert.The Task<TResult>.Result property blocks until the task has completed. Wenn die Aufgabe jedoch abgebrochen oder als fehlerhaft gekennzeichnet wurde, wird beim Versuch, auf die Result -Eigenschaft zuzugreifen, eine AggregateException -Ausnahme ausgegeben.However, if the task was canceled or faulted, attempting to access the Result property throws an AggregateException exception. Sie können dieses Problem vermeiden, indem Sie die OnlyOnRanToCompletion -Option wie im folgenden Beispiel gezeigt angeben.You can avoid this problem by using the OnlyOnRanToCompletion option, as shown in the following example.

using System;
using System.Threading.Tasks;

public class Example
{
   public static async Task Main()
   {
      var t = Task.Run( () => { DateTime dat = DateTime.Now;
                                if (dat == DateTime.MinValue)
                                   throw new ArgumentException("The clock is not working.");
                                   
                                if (dat.Hour > 17)
                                   return "evening";
                                else if (dat.Hour > 12)
                                   return "afternoon";
                                else
                                   return "morning"; });
      await t.ContinueWith( (antecedent) => { Console.WriteLine("Good {0}!",
                                                                  antecedent.Result);
                                                Console.WriteLine("And how are you this fine {0}?",
                                                                  antecedent.Result); },
                              TaskContinuationOptions.OnlyOnRanToCompletion);
   }
}
// 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)
                                 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 diese Ausnahme treffen.If you want the continuation to run even if the antecedent did not run to successful completion, you must guard against the exception. Ein Ansatz besteht darin, die Eigenschaft Task.Status des Vorgängers zu prüfen und den Zugriff auf die Eigenschaft Result nur zu versuchen, wenn der Zustand nicht Faulted oder Canceledist.One approach is to test the Task.Status property of the antecedent, and only attempt to access the Result property if the status is not Faulted or Canceled. Sie können jedoch auch die Exception -Eigenschaft des Vorgängers überprüfen.You can also examine the Exception property of the antecedent. Weitere Informationen finden Sie unter Ausnahmebehandlung (Task Parallel Library).For more information, see Exception Handling. Im folgenden Beispiel wird das vorhergehende Beispiel verändert, und der Zugriff auf die Eigenschaft Task<TResult>.Result des Vorgängers erfolgt nur, wenn dessen Zustand TaskStatus.RanToCompletionist.The following example modifies the previous example to access antecedent's Task<TResult>.Result property only if its status is TaskStatus.RanToCompletion.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var t = Task.Run( () => { DateTime dat = DateTime.Now;
                                if (dat == DateTime.MinValue)
                                   throw new ArgumentException("The clock is not working.");
                                   
                                if (dat.Hour > 17)
                                   return "evening";
                                else if (dat.Hour > 12)
                                   return "afternoon";
                                else
                                   return "morning"; });
      var c = t.ContinueWith( (antecedent) => { if (t.Status == TaskStatus.RanToCompletion) {
                                                   Console.WriteLine("Good {0}!",
                                                                     antecedent.Result);
                                                   Console.WriteLine("And how are you this fine {0}?",
                                                                  antecedent.Result);
                                                }
                                                else if (t.Status == TaskStatus.Faulted) {
                                                   Console.WriteLine(t.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?

Abbrechen einer FortsetzungCanceling a Continuation

Die Task.Status -Eigenschaft einer Fortsetzung wird in den folgenden Situationen auf TaskStatus.Canceled festgelegt:The Task.Status property of a continuation is set to TaskStatus.Canceled in the following situations:

Wenn eine Aufgabe und die zugehörige Fortsetzung zwei Teile des gleichen logischen Vorgangs sind, können Sie das gleiche Abbruchtoken an beide Aufgaben übergeben, wie im folgenden Beispiel gezeigt.If a task and its continuation represent two parts of the same logical operation, you can pass the same cancellation token to both tasks, as shown in the following example. Es besteht aus einem Vorgänger, der eine Liste mit ganzen Zahlen erstellt, die durch 33 teilbar sind und übergibt sie an die Fortsetzung.It consists of an antecedent that generates a list of integers that are divisible by 33, which it passes to the continuation. Die Fortsetzung zeigt ihrerseits die Liste an.The continuation in turn displays the list. Sowohl der Vorgänger als auch die Fortsetzung werden regelmäßig in zufälligen Intervallen angehalten.Both the antecedent and the continuation pause regularly for random intervals. Darüber hinaus wird ein System.Threading.Timer -Objekt verwendet, um die Methode Elapsed nach einem fünf Sekunden betragenden Timeoutintervall auszuführen.In addition, a System.Threading.Timer object is used to execute the Elapsed method after a five-second timeout interval. In diesem Beispiel wird die CancellationTokenSource.Cancel -Methode aufgerufen, die bewirkt, dass die aktuell ausgeführte Aufgabe die CancellationToken.ThrowIfCancellationRequested -Methode aufruft.This example calls the CancellationTokenSource.Cancel method, which causes the currently executing task to call the CancellationToken.ThrowIfCancellationRequested method. Ob die Methode CancellationTokenSource.Cancel während der Ausführung des Vorgängers oder seiner Fortsetzung aufgerufen wird, hängt von der Dauer der zufällig generierten Pausen ab.Whether the CancellationTokenSource.Cancel method is called when the antecedent or its continuation is executing depends on the duration of the randomly generated pauses. Wenn der Vorgänger abgebrochen wurde, wird die Fortsetzung nicht gestartet.If the antecedent is canceled, the continuation will not start. Wenn der Vorgänger nicht abgebrochen wird, kann das Token trotzdem zum Abbrechen der Fortsetzung verwendet werden.If the antecedent is not canceled, the token can still be used to cancel the continuation.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Random rnd = new Random();
      var cts = new CancellationTokenSource();
      CancellationToken token = cts.Token;
      Timer timer = new Timer(Elapsed, cts, 5000, Timeout.Infinite);

      var t = Task.Run( () => { List<int> product33 = new List<int>();
                                for (int ctr = 1; ctr < Int16.MaxValue; ctr++) {
                                   if (token.IsCancellationRequested) {
                                      Console.WriteLine("\nCancellation requested in antecedent...\n");
                                      token.ThrowIfCancellationRequested();
                                   }
                                   if (ctr % 2000 == 0) {
                                      int delay = rnd.Next(16,501);
                                      Thread.Sleep(delay);
                                   }

                                   if (ctr % 33 == 0)
                                      product33.Add(ctr);
                                }
                                return product33.ToArray();
                              }, token);

      Task continuation = t.ContinueWith(antecedent => { Console.WriteLine("Multiples of 33:\n");
                                                         var arr = antecedent.Result;
                                                         for (int ctr = 0; ctr < arr.Length; ctr++)
                                                         {
                                                            if (token.IsCancellationRequested) {
                                                               Console.WriteLine("\nCancellation requested in continuation...\n");
                                                               token.ThrowIfCancellationRequested();
                                                            }

                                                            if (ctr % 100 == 0) {
                                                               int delay = rnd.Next(16,251);
                                                               Thread.Sleep(delay);
                                                            }
                                                            Console.Write("{0:N0}{1}", arr[ctr],
                                                                          ctr != arr.Length - 1 ? ", " : "");
                                                            if (Console.CursorLeft >= 74)
                                                               Console.WriteLine();
                                                         }
                                                         Console.WriteLine();
                                                       } , token);

      try {
          continuation.Wait();
      }
      catch (AggregateException e) {
         foreach (Exception ie in e.InnerExceptions)
            Console.WriteLine("{0}: {1}", ie.GetType().Name,
                              ie.Message);
      }
      finally {
         cts.Dispose();
      }

      Console.WriteLine("\nAntecedent Status: {0}", t.Status);
      Console.WriteLine("Continuation Status: {0}", continuation.Status);
  }

   private static void Elapsed(object state)
   {
      CancellationTokenSource cts = state as CancellationTokenSource;
      if (cts == null) return;

      cts.Cancel();
      Console.WriteLine("\nCancellation request issued...\n");
   }
}
// The example displays the following 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,
//    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
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

Ferner können Sie die Ausführung einer Fortsetzung verhindern, wenn deren Vorgänger abgebrochen wird, ohne der Fortsetzung durch Angeben der Option TaskContinuationOptions.NotOnCanceled beim Erstellen der Fortsetzung ein Abbruchtoken zu übergeben.You can also prevent a continuation from executing if its antecedent is canceled without supplying the continuation a cancellation token by specifying the TaskContinuationOptions.NotOnCanceled option when you create the continuation. Im Folgenden finden Sie ein einfaches Beispiel.The following is a simple example.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var cts = new CancellationTokenSource();
      CancellationToken token = cts.Token;
      cts.Cancel();

      var t = Task.FromCanceled(token);
      var continuation = t.ContinueWith( (antecedent) => {
                                            Console.WriteLine("The continuation is running.");
                                          } , TaskContinuationOptions.NotOnCanceled);
      try {
         t.Wait();
      }
      catch (AggregateException ae) {
         foreach (var ie in ae.InnerExceptions)
            Console.WriteLine("{0}: {1}", ie.GetType().Name, ie.Message);

         Console.WriteLine();
      }
      finally {
         cts.Dispose();
      }

      Console.WriteLine("Task {0}: {1:G}", t.Id, t.Status);
      Console.WriteLine("Task {0}: {1:G}", continuation.Id,
                        continuation.Status);
   }
}
// The example displays the following 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 übergegangen ist, wirkt sich dies möglicherweise auf nachfolgende Fortsetzungen aus. Dies ist abhängig von den TaskContinuationOptions , die für die Fortsetzungen angegeben wurden.After a continuation goes into the Canceled state, it may affect continuations that follow, depending on the TaskContinuationOptions that were specified for those continuations.

Fortsetzungen, die verworfen wurden, werden nicht gestartet.Continuations that are disposed will not start.

Fortsetzungen und untergeordnete AufgabenContinuations and Child Tasks

Eine Fortsetzung wird erst ausgeführt, wenn der Vorgänger und all zugehörigen untergeordneten Aufgaben abgeschlossen wurden.A continuation does not run until the antecedent and all of its attached child tasks have completed. Die Fortsetzung wartet jedoch nicht, bis getrennte untergeordnete Aufgaben beendet wurden.The continuation does not wait for detached child tasks to finish. Die beiden folgenden Beispiele veranschaulichen untergeordnete Aufgaben, die an einen Vorgänger, der eine Fortsetzung erstellt, angefügt und von ihm getrennt werden.The following two examples illustrate child tasks that are attached to and detached from an antecedent that creates a continuation. Im folgenden Beispiel wird die Fortsetzung nur ausgeführt, wenn alle untergeordneten Aufgaben abgeschlossen wurden und die mehrfache Ausführung des Beispiels stets die gleiche Ausgabe erzeugt.In the following example, the continuation runs only after all child tasks have completed, and running the example multiple times produces identical output each time. Der Vorgänger im Beispiel wird durch Aufrufen der Methode TaskFactory.StartNew gestartet. Daher erstellt die Methode Task.Run standardmäßig eine übergeordnete Aufgabe, deren Standardoption zur Aufgabenerstellung TaskCreationOptions.DenyChildAttach ist.The example launches the antecedent by calling the TaskFactory.StartNew method, since by default the Task.Run method creates a parent task whose default task creation option is TaskCreationOptions.DenyChildAttach.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var t = Task.Factory.StartNew( () => { Console.WriteLine("Running antecedent task {0}...",
                                                  Task.CurrentId);
                                             Console.WriteLine("Launching attached child tasks...");
                                             for (int ctr = 1; ctr <= 5; ctr++)  {
                                                int index = ctr;
                                                Task.Factory.StartNew( (value) => {
                                                                       Console.WriteLine("   Attached child task #{0} running",
                                                                                         value);
                                                                       Thread.Sleep(1000);
                                                                     }, index, TaskCreationOptions.AttachedToParent);
                                             }
                                             Console.WriteLine("Finished launching attached child tasks...");
                                           });
      var continuation = t.ContinueWith( (antecedent) => { Console.WriteLine("Executing continuation of Task {0}",
                                                                             antecedent.Id);
                                                         });
      continuation.Wait();
   }
}
// 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
Imports System
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.If child tasks are detached from the antecedent, however, the continuation runs as soon as the antecedent has terminated, regardless of the state of the child tasks. Daher können mehrfache Ausführungen des folgenden Beispiels veränderliche Ausgaben erzeugen, die davon abhängen, wie der Taskplaner die einzelnen untergeordneten Aufgaben verarbeitet hat.As a result, multiple runs of the following example can produce variable output that depends on how the task scheduler handled each child task.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      var t = Task.Factory.StartNew( () => { Console.WriteLine("Running antecedent task {0}...",
                                                  Task.CurrentId);
                                             Console.WriteLine("Launching attached child tasks...");
                                             for (int ctr = 1; ctr <= 5; ctr++)  {
                                                int index = ctr;
                                                Task.Factory.StartNew( (value) => {
                                                                       Console.WriteLine("   Attached child task #{0} running",
                                                                                         value);
                                                                       Thread.Sleep(1000);
                                                                     }, index);
                                             }
                                             Console.WriteLine("Finished launching detached child tasks...");
                                           }, TaskCreationOptions.DenyChildAttach);
      var continuation = t.ContinueWith( (antecedent) => { Console.WriteLine("Executing continuation of Task {0}",
                                                                             antecedent.Id);
                                                         });
      continuation.Wait();
   }
}
// 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
Imports System
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.The final status of the antecedent task depends on the final status of any attached child tasks. Der Status getrennter untergeordneter Aufgaben wirkt sich nicht auf das übergeordnete Element aus.The status of detached child tasks does not affect the parent. Weitere Informationen finden Sie unter Angefügte und getrennte untergeordnete Aufgaben.For more information, see Attached and Detached Child Tasks.

Zuordnen eines Zustands zu FortsetzungenAssociating State with Continuations

Sie können einer Aufgabenfortsetzung einen die oft ausgegebene Befehlszeilen Zustand zuordnen.You can associate arbitrary state with a task continuation. Die ContinueWith -Methode stellt überladene Versionen bereit, von denen jede einen Object -Wert annimmt, der den Zustand der Fortsetzung darstellt.The ContinueWith method provides overloaded versions that each take an Object value that represents the state of the continuation. Sie können später mit der Task.AsyncState -Eigenschaft auf dieses Zustandsobjekt zugreifen.You can later access this state object by using the Task.AsyncState property. Das Zustandsobjekt ist null , wenn kein Wert angegeben wird.This state object is null if you do not provide a value.

Der Fortsetzungszustand ist bei der Verwendung der TPL nützlich, wenn Sie vorhandenen Code konvertieren, der das Asynchrone Programmiermodell (APM) verwendet.Continuation state is useful when you convert existing code that uses the Asynchronous Programming Model (APM) to use the TPL. Im APM stellen Sie in der Regel den Objektzustand in der BeginMethod-Methode bereit und greifen später mithilfe der IAsyncResult.AsyncState-Eigenschaft auf diesen Zustand zu.In the APM, you typically provide object state in the BeginMethod method and later access that state by using the IAsyncResult.AsyncState property. Mithilfe der ContinueWith -Methode können Sie diesen Zustand beibehalten, wenn Sie Code konvertieren, der das APM zur Verwendung der TPL verwendet.By using the ContinueWith method, you can preserve this state when you convert code that uses the APM to use the TPL.

Der Fortsetzungszustand kann außerdem hilfreich sein, wenn Sie mit Task-Objekten im Visual Studio-Debugger arbeiten.Continuation state can also be useful when you work with Task objects in the Visual Studio debugger. Beispielsweise wird im Fenster Parallele Aufgaben in der Spalte Aufgabe die Zeichenfolgendarstellung des Zustandsobjekts für jede Aufgabe angezeigt.For example, in the Parallel Tasks window, the Task column displays the string representation of the state object for each task. Weitere Informationen zum Fenster Parallele Aufgaben finden Sie unter Verwenden des Fensters „Aufgaben“.For more information about the Parallel Tasks window, see Using the Tasks Window.

Im folgenden Beispiel wird die Verwendung eines Fortsetzungszustands gezeigt.The following example shows how to use continuation state. Es erstellt eine Kette von Fortsetzungsaufgaben.It creates a chain of continuation tasks. Jede Aufgabe stellt die aktuelle Uhrzeit, ein DateTime -Objekt, für den state -Parameter der ContinueWith -Methode bereit.Each task provides the current time, a DateTime object, for the state parameter of the ContinueWith method. Jedes DateTime -Objekt stellt die Erstellungszeit der Fortsetzungsaufgabe dar.Each DateTime object represents the time at which the continuation task is created. Jede Aufgabe erzeugt als Ergebnis ein zweites DateTime -Objekt, das die Beendigungszeit der Aufgabe darstellt.Each task produces as its result a second DateTime object that represents the time at which the task finishes. Nach der Beendigung aller Aufgaben zeigt dieses Beispiel die Erstellungszeit und die Beendigungszeit jeder Fortsetzungsaufgabe an.After all tasks finish, this example displays the creation time and the time at which each continuation task finishes.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

// Demonstrates how to associate state with task continuations.
class ContinuationState
{
   // Simluates a lengthy operation and returns the time at which
   // the operation completed.
   public static DateTime DoWork()
   {
      // Simulate work by suspending the current thread 
      // for two seconds.
      Thread.Sleep(2000);

      // Return the current time.
      return DateTime.Now;
   }

   static void Main(string[] args)
   {
      // Start a root task that performs work.
      Task<DateTime> t = Task<DateTime>.Run(delegate { return DoWork(); });

      // Create a chain of continuation tasks, where each task is 
      // followed by another task that performs work.
      List<Task<DateTime>> continuations = new List<Task<DateTime>>();
      for (int i = 0; i < 5; i++)
      {
         // Provide the current time as the state of the continuation.
         t = t.ContinueWith(delegate { return DoWork(); }, DateTime.Now);
         continuations.Add(t);
      }

      // Wait for the last task in the chain to complete.
      t.Wait();

      // Print the creation time of each continuation (the state object)
      // and the completion time (the result of that task) to the console.
      foreach (var continuation in continuations)
      {
         DateTime start = (DateTime)continuation.AsyncState;
         DateTime end = continuation.Result;

         Console.WriteLine("Task was created at {0} and finished at {1}.",
            start.TimeOfDay, end.TimeOfDay);
      }
   }
}

/* Sample 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
   ' Simluates 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.

Behandeln von durch Fortsetzungen ausgelösten AusnahmenHandling Exceptions Thrown from Continuations

Eine Vorgänger-Fortsetzung-Beziehung ist keine Beziehung zwischen übergeordneten und untergeordneten Elementen.An antecedent-continuation relationship is not a parent-child relationship. Durch Fortsetzungen ausgelöste Ausnahmen werden nicht an den Vorgänger weitergegeben.Exceptions thrown by continuations are not propagated to the antecedent. Behandeln Sie Ausnahmen, die von Fortsetzungen ausgelöst wurden, daher wie bei allen anderen Aufgaben (siehe unten):Therefore, handle exceptions thrown by continuations as you would handle them in any other task, as follows:

  • Verwenden Sie die Methode Wait, WaitAlloder WaitAny bzw. die generische Entsprechung, um auf die Fortsetzung zu warten.You can use the Wait, WaitAll, or WaitAny method, or its generic counterpart, to wait on the continuation. Sie können in der gleichen try -Anweisung auf einen Vorgänger und seine Fortsetzungen warten, wie im folgenden Beispiel gezeigt.You can wait for an antecedent and its continuations in the same try statement, as shown in the following example.

    using System;
    using System.Threading.Tasks;
    
    public class Example
    {
       public static void Main()
       {
          var task1 = Task<int>.Run( () => { Console.WriteLine("Executing task {0}",
                                                               Task.CurrentId);
                                             return 54; });
          var continuation = task1.ContinueWith( (antecedent) =>
                                                 { Console.WriteLine("Executing continuation task {0}",
                                                                     Task.CurrentId);
                                                   Console.WriteLine("Value from antecedent: {0}",
                                                                     antecedent.Result);
                                                   throw new InvalidOperationException();
                                                } );
    
          try {
             task1.Wait();
             continuation.Wait();
          }
          catch (AggregateException ae) {
              foreach (var ex in ae.InnerExceptions)
                  Console.WriteLine(ex.Message);
          }
       }
    }
    // 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.
    
    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.You can use a second continuation to observe the Exception property of the first continuation. In folgenden Beispiel versucht eine Aufgabe, aus einer nicht vorhandenen Datei zu lesen.In the following example, a task attempts to read from a non-existent file. In einem solchen Fall zeigt die Fortsetzung Informationen über die Ausnahme in der vorhergehenden Aufgabe an.The continuation then displays information about the exception in the antecedent task.

    using System;
    using System.IO;
    using System.Threading.Tasks;
    
    public class Example
    {
       public static void Main()
       {
          var t = Task.Run( () => { string s = File.ReadAllText(@"C:\NonexistentFile.txt");
                                    return s;
                                  });
    
          var c = t.ContinueWith( (antecedent) =>
                                  { // Get the antecedent's exception information.
                                    foreach (var ex in antecedent.Exception.InnerExceptions) {
                                       if (ex is FileNotFoundException)
                                          Console.WriteLine(ex.Message);
                                    }
                                  }, TaskContinuationOptions.OnlyOnFaulted);
    
          c.Wait();
       }
    }
    // 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 die Ausführung mit der Option TaskContinuationOptions.OnlyOnFaulted erfolgt ist, wird die Fortsetzung nur ausgeführt, wenn im Vorgänger eine Ausnahme auftritt, daher kann sie davon ausgehen, dass die Eigenschaft Exception des Vorgängers nicht nullist.Because it was run with the TaskContinuationOptions.OnlyOnFaulted option, the continuation executes only if an exception occurs in the antecedent, and therefore it can assume that the antecedent's Exception property is not null. Wenn die Fortsetzung unabhängig davon ausgeführt wird, ob im Vorgänger eine Ausnahme ausgelöst wurde, muss entsprechend eine Prüfung darauf erfolgen, dass die Eigenschaft Exception des Vorgängers nicht null ist, bevor versucht wird, die Ausnahme zu behandeln, wie im folgenden Beispiel dargestellt.If the continuation executes whether or not an exception is thrown in the antecedent, it would have to check whether the antecedent's Exception property is not null before attempting to handle the exception, as the following code fragment shows.

    // Determine whether an exception occurred.
    if (antecedent.Exception != null) {
       foreach (var ex in antecedent.Exception.InnerExceptions) {
          if (ex is FileNotFoundException)
             Console.WriteLine(ex.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 (Task Parallel Library).For more information, see Exception Handling.

  • 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.If the continuation is an attached child task that was created by using the TaskContinuationOptions.AttachedToParent option, its exceptions will be propagated by the parent back to the calling thread, as is the case in any other attached child. Weitere Informationen finden Sie unter Angefügte und getrennte untergeordnete Aufgaben.For more information, see Attached and Detached Child Tasks.

Siehe auchSee also