Řetězení úloh pomocí úloh pokračováníChaining Tasks by Using Continuation Tasks

V asynchronním programování je běžné pro jednu asynchronní operaci po dokončení, k vyvolání druhé operace a předání dat do ní.In asynchronous programming, it is common for one asynchronous operation, on completion, to invoke a second operation and pass data to it. Pokračování je tradičně prováděno pomocí metod zpětného volání.Traditionally, continuations have been done by using callback methods. V knihovně Task Parallel Library jsou stejné funkce poskytovány pokračujícími úkoly.In the Task Parallel Library, the same functionality is provided by continuation tasks. Pokračující úkol (označuje se také jako pokračování) je asynchronní úloha, která je vyvolána jinou úlohou, která se označuje jako předchůdcepo dokončení předchůdce.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.

Pokračování je poměrně snadné použít, ale jsou ale výkonné a flexibilní.Continuations are relatively easy to use, but are nevertheless powerful and flexible. Můžete například:For example, you can:

  • Předat data z předchůdce do pokračování.Pass data from the antecedent to the continuation.

  • Určete přesné podmínky, za kterých bude pokračování vyvoláno nebo není vyvoláno.Specify the precise conditions under which the continuation will be invoked or not invoked.

  • Zrušení pokračování buď před jeho spuštěním, nebo ve spolupráci s tím, jak běží.Cancel a continuation either before it starts or cooperatively as it is running.

  • Poskytněte nápovědu, jak má být pokračování naplánováno.Provide hints about how the continuation should be scheduled.

  • Vyvolá několik pokračování ze stejného předchůdce.Invoke multiple continuations from the same antecedent.

  • Vyvolat jedno pokračování, až se dokončí všechny nebo některé z více předchůdců.Invoke one continuation when all or any one of multiple antecedents complete.

  • Zřetězit pokračování jedno po druhém do jakékoli libovolné délky.Chain continuations one after another to any arbitrary length.

  • Použijte pokračování pro zpracování výjimek vyvolaných předchůdcem.Use a continuation to handle exceptions thrown by the antecedent.

O pokračováníAbout continuations

Pokračování je úkol, který je vytvořen ve stavu WaitingForActivation.A continuation is a task that is created in the WaitingForActivation state. Aktivuje se automaticky po dokončení jeho předchozí úlohy nebo úkolů.It is activated automatically when its antecedent task or tasks complete. Volání Task.Start při pokračování v uživatelském kódu vyvolá výjimku System.InvalidOperationException.Calling Task.Start on a continuation in user code throws an System.InvalidOperationException exception.

Pokračování je samo Task a neblokuje vlákno, ve kterém je spuštěno.A continuation is itself a Task and does not block the thread on which it is started. Zavolejte metodu Task.Wait pro blokování, dokud neskončí pokračování úlohy.Call the Task.Wait method to block until the continuation task finishes.

Vytvoření pokračování pro jeden předchůdceCreating a continuation for a single antecedent

Můžete vytvořit pokračování, které se provede po dokončení jeho předchůdce voláním metody Task.ContinueWith.You create a continuation that executes when its antecedent has completed by calling the Task.ContinueWith method. Následující příklad znázorňuje základní vzorek (pro přehlednost, vynechává se zpracování výjimek).The following example shows the basic pattern (for clarity, exception handling is omitted). Spustí předchozí úlohu, taskA, která vrátí objekt DayOfWeek, který označuje název aktuálního dne v týdnu.It executes an antecedent task, taskA, that returns a DayOfWeek object that indicates the name of the current day of the week. Po dokončení předchůdce se úkol pokračování, continuation, předává předchůdci a zobrazí řetězec, který obsahuje jeho výsledek.When the antecedent completes, the continuation task, continuation, is passed the antecedent and displays a string that includes its result.

Poznámka

C# Ukázky v tomto článku využívají modifikátor async na metodě Main.The C# samples in this article make use of the async modifier on the Main method. Tato funkce je k dispozici v C# 7,1 a novějších verzích.That feature is available in C# 7.1 and later. Předchozí verze generují CS5001 při kompilování tohoto ukázkového kódu.Previous versions generate CS5001 when compiling this sample code. Je nutné nastavit jazykovou verzi na C# 7,1 nebo novější.You'll need to set the language version to C# 7.1 or newer. Informace o tom, jak nakonfigurovat jazykovou verzi, najdete v článku o konfiguraci jazykové verze.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.

Vytváření pokračování pro více předchůdcůCreating a continuation for multiple antecedents

Můžete také vytvořit pokračování, které se spustí, když se dokončí kterákoli nebo celá skupina úkolů.You can also create a continuation that will run when any or all of a group of tasks has completed. Chcete-li provést pokračování po dokončení všech předchozích úloh, zavoláte metodu static (Shared in Visual Basic) Task.WhenAll nebo metodu instance TaskFactory.ContinueWhenAll.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. Chcete-li provést pokračování po dokončení kteréhokoliv z předchozích úloh, zavoláte metodu static (Shared in Visual Basic) Task.WhenAny nebo metodu instance TaskFactory.ContinueWhenAny.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.

Všimněte si, že volání Task.WhenAll a Task.WhenAny přetížení neblokují volající vlákno.Note that calls to the Task.WhenAll and Task.WhenAny overloads do not block the calling thread. Nicméně obvykle zavoláte všechny metody, ale Task.WhenAll(IEnumerable<Task>) a Task.WhenAll(Task[]), abyste načetli vrácenou vlastnost Task<TResult>.Result, která zablokuje volající vlákno.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.

V následujícím příkladu je volána metoda Task.WhenAll(IEnumerable<Task>) pro vytvoření pokračování úlohy, která odráží výsledky jeho 10 předchozích úloh.The following example calls the Task.WhenAll(IEnumerable<Task>) method to create a continuation task that reflects the results of its 10 antecedent tasks. Každý předchozí úkol napředá hodnotu indexu, která je v rozsahu od 1 do 10.Each antecedent task squares an index value that ranges from one to 10. Pokud se předchůdce úspěšně dokončí (vlastnost Task.Status je TaskStatus.RanToCompletion), vlastnost Task<TResult>.Result pokračování je pole hodnot Task<TResult>.Result vrácených jednotlivými předchůdci.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. Tento příklad je přidá k výpočtu součtu čtverců pro všechna čísla od 1 do 10.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

Možnosti pokračováníContinuation options

Když vytvoříte pokračování jedné úlohy, můžete použít ContinueWith přetížení, které přebírá hodnotu System.Threading.Tasks.TaskContinuationOptions výčtu k určení podmínek, za kterých se pokračování spustí.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. Můžete například určit, že pokračování bude spuštěno pouze v případě, že se předchůdce úspěšně dokončí, nebo pouze v případě, že se dokončí v chybovém stavu.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. Pokud podmínka není pravdivá, pokud je předchůdce připraven k vyvolání pokračování, pokračování přejde přímo do stavu TaskStatus.Canceled a následně nemůže být spuštěno.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.

Několik metod pokračování více úloh, jako je například přetížení metody TaskFactory.ContinueWhenAll, zahrnuje také parametr 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. Platná je však pouze podmnožina všech členů výčtu System.Threading.Tasks.TaskContinuationOptions.Only a subset of all System.Threading.Tasks.TaskContinuationOptions enumeration members are valid, however. Můžete určit System.Threading.Tasks.TaskContinuationOptions hodnoty, které mají protějšky ve výčtu System.Threading.Tasks.TaskCreationOptions, například TaskContinuationOptions.AttachedToParent, TaskContinuationOptions.LongRunninga 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. Pokud zadáte některou z NotOn nebo OnlyOn možnosti s pokračováním více úloh, bude vyvolána výjimka ArgumentOutOfRangeException v době běhu.If you specify any of the NotOn or OnlyOn options with a multi-task continuation, an ArgumentOutOfRangeException exception will be thrown at run time.

Další informace o možnostech pokračování úlohy najdete v tématu TaskContinuationOptions.For more information on task continuation options, see the TaskContinuationOptions topic.

Předávání dat do pokračováníPassing Data to a Continuation

Metoda Task.ContinueWith předá odkaz na předchůdce jako argument pro uživatele pokračování.The Task.ContinueWith method passes a reference to the antecedent to the user delegate of the continuation as an argument. Pokud je předchůdce objekt System.Threading.Tasks.Task<TResult> a úloha byla spuštěna až do dokončení, pokračování může získat přístup k vlastnosti Task<TResult>.Result úlohy.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.

Vlastnost Task<TResult>.Result zablokuje, dokud se úloha nedokončí.The Task<TResult>.Result property blocks until the task has completed. Pokud se ale úloha zrušila nebo došlo k chybě, pokus o přístup k vlastnosti Result vyvolá výjimku AggregateException.However, if the task was canceled or faulted, attempting to access the Result property throws an AggregateException exception. Tomuto problému se můžete vyhnout pomocí možnosti OnlyOnRanToCompletion, jak je znázorněno v následujícím příkladu.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?

Pokud chcete pokračovat v běhu i v případě, že předchůdce nebyl úspěšně dokončen, je nutné před výjimkou provést ochranu.If you want the continuation to run even if the antecedent did not run to successful completion, you must guard against the exception. Jedním z přístupů je otestování vlastnosti Task.Status předchůdce a pokus o přístup k vlastnosti Result pouze v případě, že stav není Faulted nebo Canceled.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. Můžete také prostudovat vlastnost Exception předchůdce.You can also examine the Exception property of the antecedent. Další informace naleznete v tématu zpracování výjimek.For more information, see Exception Handling. Následující příklad upravuje předchozí příklad pro přístup k vlastnosti Task<TResult>.Result předchůdce pouze v případě, že je jeho stav TaskStatus.RanToCompletion.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?

Zrušení pokračováníCanceling a Continuation

Vlastnost Task.Status pokračování je nastavena na TaskStatus.Canceled v následujících situacích:The Task.Status property of a continuation is set to TaskStatus.Canceled in the following situations:

Pokud úloha a její pokračování reprezentují dvě části stejné logické operace, můžete stejný token zrušení předat oběma úkolům, jak je znázorněno v následujícím příkladu.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. Skládá se z předchůdce, který generuje seznam celých čísel, která jsou dělitelná 33, která je předána pokračování.It consists of an antecedent that generates a list of integers that are divisible by 33, which it passes to the continuation. Pokračování zobrazí seznam.The continuation in turn displays the list. Předchůdce i pokračování v pravidelných intervalech se v náhodných intervalech pozastaví.Both the antecedent and the continuation pause regularly for random intervals. Kromě toho se System.Threading.Timer objekt používá ke spuštění metody Elapsed po pěti sekundách intervalu časového limitu.In addition, a System.Threading.Timer object is used to execute the Elapsed method after a five-second timeout interval. Tento příklad volá metodu CancellationTokenSource.Cancel, která způsobuje, že aktuálně vykonávaná úloha volá metodu CancellationToken.ThrowIfCancellationRequested.This example calls the CancellationTokenSource.Cancel method, which causes the currently executing task to call the CancellationToken.ThrowIfCancellationRequested method. Určuje, zda je volána metoda CancellationTokenSource.Cancel, když předchůdce nebo jeho pokračování provádí, závisí na době trvání náhodně generovaných pozastavení.Whether the CancellationTokenSource.Cancel method is called when the antecedent or its continuation is executing depends on the duration of the randomly generated pauses. Pokud je předchůdce zrušen, pokračování nebude zahájeno.If the antecedent is canceled, the continuation will not start. Pokud předchůdce není zrušen, token lze nadále použít k zrušení pokračování.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

Můžete také zabránit spuštění pokračování, pokud je jeho předchůdce zrušen bez zadání pokračování tokenu zrušení zadáním možnosti TaskContinuationOptions.NotOnCanceled při vytváření pokračování.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. Následuje jednoduchý příklad.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

Po pokračování přejde do stavu Canceled, může ovlivnit pokračování, která následují, v závislosti na TaskContinuationOptions, které byly pro tyto pokračování zadány.After a continuation goes into the Canceled state, it may affect continuations that follow, depending on the TaskContinuationOptions that were specified for those continuations.

Pokračování, která jsou uvolněna, se nespustí.Continuations that are disposed will not start.

Pokračování a podřízené úlohyContinuations and Child Tasks

Pokračování není spuštěno, dokud předchůdce a všechny jeho připojené podřízené úlohy nebyly dokončeny.A continuation does not run until the antecedent and all of its attached child tasks have completed. Pokračování nečeká na dokončení odpojených podřízených úloh.The continuation does not wait for detached child tasks to finish. Následující dva příklady ilustrují podřízené úlohy, které jsou připojeny k a odpojeny od předchůdce, který vytváří pokračování.The following two examples illustrate child tasks that are attached to and detached from an antecedent that creates a continuation. V následujícím příkladu se pokračování spustí až po dokončení všech podřízených úloh a spuštění příkladu několikrát vytvoří stejný výstup.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. V příkladu se spustí předchůdce voláním metody TaskFactory.StartNew, protože ve výchozím nastavení metoda Task.Run vytvoří nadřazenou úlohu, jejíž výchozí možnost vytvoření úlohy je TaskCreationOptions.DenyChildAttach.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.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

Pokud se ale podřízené úlohy odpojí od předchůdce, pokračování se spustí hned po ukončení předchůdce, a to bez ohledu na stav podřízených úloh.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. V důsledku toho může více spuštění z následujícího příkladu vytvořit proměnnou výstup, který závisí na tom, jak Plánovač úloh zpracoval každou podřízenou úlohu.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.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

Konečný stav předchozí úlohy závisí na konečném stavu všech připojených podřízených úloh.The final status of the antecedent task depends on the final status of any attached child tasks. Stav odpojených podřízených úloh nemá vliv na nadřazenou položku.The status of detached child tasks does not affect the parent. Další informace najdete v tématu připojené a odpojené podřízené úlohy.For more information, see Attached and Detached Child Tasks.

Přidružení stavu k pokračovánímAssociating State with Continuations

Můžete přidružit libovolný stav pokračování úlohy.You can associate arbitrary state with a task continuation. Metoda ContinueWith poskytuje přetížené verze, které každá z nich převezme hodnotu Object, která představuje stav pokračování.The ContinueWith method provides overloaded versions that each take an Object value that represents the state of the continuation. Později můžete přistupovat k tomuto objektu stavu pomocí vlastnosti Task.AsyncState.You can later access this state object by using the Task.AsyncState property. Tento objekt stavu je null, pokud nezadáte hodnotu.This state object is null if you do not provide a value.

Pokračování stavu je užitečné, pokud převedete existující kód, který používá asynchronní programovací model (APM) pro použití rozhraní TPL.Continuation state is useful when you convert existing code that uses the Asynchronous Programming Model (APM) to use the TPL. V APM obvykle poskytujete stav objektu v metodě Beginmetody a později získáte přístup k tomuto stavu pomocí vlastnosti IAsyncResult.AsyncState.In the APM, you typically provide object state in the BeginMethod method and later access that state by using the IAsyncResult.AsyncState property. Pomocí metody ContinueWith můžete zachovat tento stav při převodu kódu, který používá APM k použití rozhraní TPL.By using the ContinueWith method, you can preserve this state when you convert code that uses the APM to use the TPL.

Stav pokračování může být užitečné také při práci s Task objekty v ladicím programu sady Visual Studio.Continuation state can also be useful when you work with Task objects in the Visual Studio debugger. Například v okně Paralelní úlohy zobrazuje sloupec úkoly řetězcovou reprezentaci objektu State pro každý úkol.For example, in the Parallel Tasks window, the Task column displays the string representation of the state object for each task. Další informace o okně Paralelní úlohy najdete v tématu použití okna úlohy.For more information about the Parallel Tasks window, see Using the Tasks Window.

Následující příklad ukazuje, jak použít stav pokračování.The following example shows how to use continuation state. Vytvoří řetězec pokračujících úloh.It creates a chain of continuation tasks. Každý úkol poskytuje aktuální čas, objekt DateTime pro parametr state metody ContinueWith.Each task provides the current time, a DateTime object, for the state parameter of the ContinueWith method. Každý objekt DateTime představuje čas, kdy se vytvoří úkol pokračování.Each DateTime object represents the time at which the continuation task is created. Každý úkol vytváří jako výsledek druhý objekt DateTime, který představuje čas, kdy se úkol dokončí.Each task produces as its result a second DateTime object that represents the time at which the task finishes. Po dokončení všech úloh v tomto příkladu se zobrazí čas vytvoření a čas, kdy se Každá úloha pokračování dokončí.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.

Zpracování výjimek vyvolaných z pokračováníHandling Exceptions Thrown from Continuations

Vztah předchůdce-pokračování není relace typu nadřazený-podřízený.An antecedent-continuation relationship is not a parent-child relationship. Výjimky vyvolané pokračováním nejsou šířeny do předchůdce.Exceptions thrown by continuations are not propagated to the antecedent. Proto zpracujte výjimky vyvolané pokračováním, protože byste je měli zpracovat v jakékoli jiné úloze, a to následujícím způsobem:Therefore, handle exceptions thrown by continuations as you would handle them in any other task, as follows:

  • Pro čekání na pokračování lze použít metodu Wait, WaitAllnebo WaitAny nebo její obecné protějšky.You can use the Wait, WaitAll, or WaitAny method, or its generic counterpart, to wait on the continuation. Můžete počkat na předchůdce a jeho pokračování ve stejném příkazu try, jak je znázorněno v následujícím příkladu.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.
    
  • Druhé pokračování můžete použít ke sledování vlastnosti Exception prvního pokračování.You can use a second continuation to observe the Exception property of the first continuation. V následujícím příkladu se úloha pokusí o čtení z neexistujícího souboru.In the following example, a task attempts to read from a non-existent file. Pokračování potom zobrazí informace o výjimce v předchozí úloze.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'.
    

    Protože byl spuštěn s možností TaskContinuationOptions.OnlyOnFaulted, pokračování se spustí pouze v případě, že v předchůdci dojde k výjimce, a proto může předpokládat, že vlastnost Exception předchůdce není null.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. Pokud se pokračování provede bez ohledu na to, zda je vyvolána výjimka v předchůdci, bude nutné ověřit, zda je před pokusem o zpracování výjimky nenullá vlastnost Exception předchůdce, jak ukazuje následující fragment kódu.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
    

    Další informace naleznete v tématu zpracování výjimek.For more information, see Exception Handling.

  • Pokud je pokračování připojená podřízená úloha, která byla vytvořena pomocí možnosti TaskContinuationOptions.AttachedToParent, jejich výjimky budou rozšířeny nadřazeným objektem zpět do volajícího vlákna, jako je případ v jakémkoli jiném připojeném podřízeném prvku.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. Další informace najdete v tématu připojené a odpojené podřízené úlohy.For more information, see Attached and Detached Child Tasks.

Viz také:See also