Aufgabenbasiertes asynchrones ProgrammierenTask-based asynchronous programming

Die Task Parallel Library (TPL) basiert auf dem Konzept einer Aufgabe, die einen asynchronen Vorgang darstellt.The Task Parallel Library (TPL) is based on the concept of a task, which represents an asynchronous operation. In einigen Dingen ist eine Aufgabe vergleichbar mit einer ThreadPool, weist jedoch eine höhere Abstraktionsebene auf.In some ways, a task resembles a thread or ThreadPool work item, but at a higher level of abstraction. Der Begriff Aufgabenparallelität bezeichnet eine oder mehrere eigenständige Aufgaben, die gleichzeitig ausgeführt werden.The term task parallelism refers to one or more independent tasks running concurrently. Aufgaben bieten zwei Hauptvorteile:Tasks provide two primary benefits:

  • Effiziente und skalierbare Verwendung von Systemressourcen.More efficient and more scalable use of system resources.

    Aufgaben werden im Hintergrund in ThreadPool eingereicht, der mit Algorithmen verbessert wurde, durch die die Anzahl von Threads bestimmt und angepasst wird und die einen Lastenausgleich verfügbar machen, der den Durchsatz maximiert.Behind the scenes, tasks are queued to the ThreadPool, which has been enhanced with algorithms that determine and adjust to the number of threads and that provide load balancing to maximize throughput. Aufgaben sind hierdurch relativ einfach strukturiert, und Sie können für eine differenzierte Parallelität viele Aufgaben erstellen.This makes tasks relatively lightweight, and you can create many of them to enable fine-grained parallelism.

  • Stärker programmgesteuerte Kontrolle als bei Threads oder Arbeitselementen.More programmatic control than is possible with a thread or work item.

    Aufgaben und das diese umgebende Framework stellen einen umfangreichen Satz von APIs bereit, die Warten, Abbruch, Fortsetzungen, robuste Ausnahmebehandlung, detaillierte Zustandsangaben, benutzerdefinierte Planung und Vieles mehr unterstützen.Tasks and the framework built around them provide a rich set of APIs that support waiting, cancellation, continuations, robust exception handling, detailed status, custom scheduling, and more.

Aus diesen Gründen ist TPL in .NET Framework die bevorzugte API zum Schreiben von asynchronem, parallelem und Multithreadcode.For both of these reasons, in the .NET Framework, TPL is the preferred API for writing multi-threaded, asynchronous, and parallel code.

Implizites Erstellen und Ausführen von AufgabenCreating and running tasks implicitly

Die Parallel.Invoke-Methode bietet eine einfache Möglichkeit, eine beliebige Anzahl willkürlicher Anweisungen gleichzeitig auszuführen.The Parallel.Invoke method provides a convenient way to run any number of arbitrary statements concurrently. Sie müssen nur einen Action-Delegaten für jede Arbeitsaufgabe übergeben.Just pass in an Action delegate for each item of work. Die einfachste Möglichkeit, diese Delegaten zu erstellen, sind Lambdaausdrücke.The easiest way to create these delegates is to use lambda expressions. Der Lambdaausdruck kann entweder eine benannte Methode aufrufen oder den Code inline bereitstellen.The lambda expression can either call a named method or provide the code inline. Im folgenden Beispiel wird ein grundlegender Invoke-Aufruf dargestellt, der zwei Aufgaben erstellt und startet, die gleichzeitig ausgeführt werden.The following example shows a basic Invoke call that creates and starts two tasks that run concurrently. Die erste Aufgabe wird durch einen Lambda-Ausdruck dargestellt, der die DoSomeWork-Methode aufruft, und die zweite Aufgabe wird durch einen Lambda-Ausdruck dargestellt, der die DoSomeOtherWork-Methode aufruft.The first task is represented by a lambda expression that calls a method named DoSomeWork, and the second task is represented by a lambda expression that calls a method named DoSomeOtherWork.

Hinweis

Diese Dokumentation definiert Delegaten in TPL mithilfe von Lambdaausdrücken.This documentation uses lambda expressions to define delegates in TPL. Falls Sie mit der Verwendung von Lambda-Ausdrücken in C# oder Visual Basic nicht vertraut sind, finden Sie entsprechende Informationen unter Lambda Expressions in PLINQ and TPL (Lambda-Ausdrücke in PLINQ und TPL).If you are not familiar with lambda expressions in C# or Visual Basic, see Lambda Expressions in PLINQ and TPL.

Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())

Hinweis

Die Anzahl von Task-Instanzen, die im Hintergrund von Invoke erstellt werden, ist nicht notwendigerweise mit der Anzahl der bereitgestellten Delegaten identisch.The number of Task instances that are created behind the scenes by Invoke is not necessarily equal to the number of delegates that are provided. Die TPL kann verschiedene Optimierungen einsetzen, besonders bei einer großen Anzahl von Delegaten.The TPL may employ various optimizations, especially with large numbers of delegates.

Weitere Informationen finden Sie unter Vorgehensweise: Ausführen von parallelen Vorgängen mithilfe von „Parallel.Invoke“.For more information, see How to: Use Parallel.Invoke to Execute Parallel Operations.

Wenn Sie die Aufgabenausführung präziser steuern oder einen Wert aus der Aufgabe zurückgeben möchten, müssen Sie expliziter mit Task-Objekten arbeiten.For greater control over task execution or to return a value from the task, you have to work with Task objects more explicitly.

Explizites Erstellen und Ausführen von AufgabenCreating and running tasks explicitly

Eine Aufgabe, die keinen Wert zurückgibt, wird durch die System.Threading.Tasks.Task-Klasse dargestellt.A task that does not return a value is represented by the System.Threading.Tasks.Task class. Eine Aufgabe, die einen Wert zurückgibt, wird durch die System.Threading.Tasks.Task<TResult>-Klasse dargestellt, die von Task erbt.A task that returns a value is represented by the System.Threading.Tasks.Task<TResult> class, which inherits from Task. Das Aufgabenobjekt verarbeitet die Infrastrukturdetails und stellt Methoden sowie Eigenschaften bereit, auf die während der gesamten Lebensdauer der Aufgabe vom aufrufenden Thread aus zugegriffen werden kann.The task object handles the infrastructure details and provides methods and properties that are accessible from the calling thread throughout the lifetime of the task. Sie können beispielsweise jederzeit auf die Status-Eigenschaft einer Aufgabe zugreifen, um zu ermitteln, ob die Ausführung gestartet, abgeschlossen oder abgebrochen wurde bzw. ob eine Ausnahme ausgelöst wurde.For example, you can access the Status property of a task at any time to determine whether it has started running, ran to completion, was canceled, or has thrown an exception. Der Status wird durch eine TaskStatus-Enumeration dargestellt.The status is represented by a TaskStatus enumeration.

Wenn Sie eine Aufgabe erstellen, weisen Sie dieser einen Benutzerdelegaten zu, der den von der Aufgabe ausgeführten Code kapselt.When you create a task, you give it a user delegate that encapsulates the code that the task will execute. Der Delegat kann als benannter Delegat, als anonyme Methode oder als Lambda-Ausdruck angegeben werden.The delegate can be expressed as a named delegate, an anonymous method, or a lambda expression. Lambdaausdrücke können einen Aufruf einer benannten Methode enthalten, wie im folgenden Beispiel gezeigt.Lambda expressions can contain a call to a named method, as shown in the following example. Beachten Sie, dass im Beispiel ein Aufruf der Task.Wait-Methode enthalten ist, um sicherzustellen, dass die Aufgabe abgeschlossen ist, ehe die Konsolenmodusanwendung beendet wird.Note that the example includes a call to the Task.Wait method to ensure that the task completes execution before the console mode application ends.

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

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Create a task and supply a user delegate by using a lambda expression. 
      Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
      // Start the task.
      taskA.Start();

      // Output a message from the calling thread.
      Console.WriteLine("Hello from thread '{0}'.", 
                        Thread.CurrentThread.Name);
      taskA.Wait();
   }
}
// The example displays output like the following:
//       Hello from thread 'Main'.
//       Hello from taskA.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
        Thread.CurrentThread.Name = "Main"
        
        ' Create a task and supply a user delegate by using a lambda expression. 
        Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))
        ' Start the task.
        taskA.Start()

        ' Output a message from the calling thread.
        Console.WriteLine("Hello from thread '{0}'.", 
                          Thread.CurrentThread.Name)
        taskA.Wait()
   End Sub
End Module
' The example displays output like the following:
'    Hello from thread 'Main'.
'    Hello from taskA.

Sie können auch die Task.Run-Methoden verwenden, um eine Aufgabe in einem Vorgang zu erstellen und zu starten.You can also use the Task.Run methods to create and start a task in one operation. Zum Verwalten der Aufgabe verwenden die Run-Methoden den Standardaufgabenplaner, unabhängig von dem Aufgabenplaner, der dem aktuellen Thread zugewiesen ist.To manage the task, the Run methods use the default task scheduler, regardless of which task scheduler is associated with the current thread. Wenn eine präzisere Steuerung der Erstellung und Planung von Aufgaben nicht erforderlich ist, sollten diese vorzugsweise mit den Run-Methoden erstellt und gestartet werden.The Run methods are the preferred way to create and start tasks when more control over the creation and scheduling of the task is not needed.

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

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Define and run the task.
      Task taskA = Task.Run( () => Console.WriteLine("Hello from taskA."));

      // Output a message from the calling thread.
      Console.WriteLine("Hello from thread '{0}'.", 
                          Thread.CurrentThread.Name);
      taskA.Wait();
   }
}
// The example displays output like the following:
//       Hello from thread 'Main'.
//       Hello from taskA.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
        Thread.CurrentThread.Name = "Main"
        
        Dim taskA As Task = Task.Run(Sub() Console.WriteLine("Hello from taskA."))

        ' Output a message from the calling thread.
        Console.WriteLine("Hello from thread '{0}'.", 
                          Thread.CurrentThread.Name)
        taskA.Wait()
   End Sub
End Module
' The example displays output like the following:
'    Hello from thread 'Main'.
'    Hello from taskA.

Sie können auch die TaskFactory.StartNew-Methode verwenden, um eine Aufgabe in einem Schritt zu erstellen und zu starten.You can also use the TaskFactory.StartNew method to create and start a task in one operation. Verwenden Sie diese Methode, wenn Erstellung und Planung nicht getrennt werden müssen, zusätzliche Aufgabenerstellungsoptionen oder die Nutzung eines bestimmten Planers erforderlich sind oder der Aufgabe zusätzliche Zustände übergeben werden müssen, die über deren Task.AsyncState-Eigenschaft abgerufen werden können, wie im folgenden Beispiel dargestellt.Use this method when creation and scheduling do not have to be separated and you require additional task creation options or the use of a specific scheduler, or when you need to pass additional state into the task that you can retrieve through its Task.AsyncState property, as shown in the following example.

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

class CustomData
{
   public long CreationTime;
   public int Name; 
   public int ThreadNum;
}

public class Example
{
   public static void Main()
   {
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;
                                     
                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
      foreach (var task in taskArray) {
         var data = task.AsyncState as CustomData;
         if (data != null)
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum);
      }                     
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

Class CustomData
   Public CreationTime As Long
   Public Name As Integer
   Public ThreadNum As Integer
End Class

Module Example
   Public Sub Main()
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As CustomData = TryCast(obj, CustomData)
                                                 If data Is Nothing Then Return
                                     
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                              End Sub,
                                              New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} )
      Next
      Task.WaitAll(taskArray)     

      For Each task In taskArray
         Dim data = TryCast(task.AsyncState, CustomData)
         If data IsNot Nothing Then
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum)
         End If
      Next
   End Sub
End Module
' The example displays output like the following:
'    Task #0 created at 635116451245250515, ran on thread #3, RanToCompletion
'    Task #1 created at 635116451245270515, ran on thread #4, RanToCompletion
'    Task #2 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #3 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #4 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #5 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #6 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #7 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #8 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #9 created at 635116451245270515, ran on thread #3, RanToCompletion

Task und Task<TResult> machen jeweils eine statische Factory-Eigenschaft verfügbar, von der eine Standardinstanz von TaskFactory zurückgegeben wird, sodass Sie die Methode als Task.Factory.StartNew() aufrufen können.Task and Task<TResult> each expose a static Factory property that returns a default instance of TaskFactory, so that you can call the method as Task.Factory.StartNew(). Darüber hinaus verfügen die Aufgaben im Beispiel, da sie vom System.Threading.Tasks.Task<TResult>-Typ sind, jeweils über eine öffentliche Task<TResult>.Result-Eigenschaft, die das Ergebnis der Berechnung enthält.Also, in the following example, because the tasks are of type System.Threading.Tasks.Task<TResult>, they each have a public Task<TResult>.Result property that contains the result of the computation. Die Aufgaben werden asynchron ausgeführt und können in einer beliebigen Reihenfolge abgeschlossen werden.The tasks run asynchronously and may complete in any order. Wenn auf die Result-Eigenschaft zugegriffen wird, ehe die Berechnung beendet wurde, sperrt die Eigenschaft den aufrufenden Thread, bis der Wert verfügbar ist.If the Result property is accessed before the computation finishes, the property blocks the calling thread until the value is available.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
        Task<Double>[] taskArray = { Task<Double>.Factory.StartNew(() => DoComputation(1.0)),
                                     Task<Double>.Factory.StartNew(() => DoComputation(100.0)), 
                                     Task<Double>.Factory.StartNew(() => DoComputation(1000.0)) };

        var results = new Double[taskArray.Length];
        Double sum = 0;
        
        for (int i = 0; i < taskArray.Length; i++) {
            results[i] = taskArray[i].Result;
            Console.Write("{0:N1} {1}", results[i], 
                              i == taskArray.Length - 1 ? "= " : "+ ");
            sum += results[i];
        }
        Console.WriteLine("{0:N1}", sum);
   }

   private static Double DoComputation(Double start)
   {
      Double sum = 0;
      for (var value = start; value <= start + 10; value += .1)
         sum += value;

      return sum; 
   }
}
// The example displays the following output:
//        606.0 + 10,605.0 + 100,495.0 = 111,706.0
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
        Dim taskArray() = { Task(Of Double).Factory.StartNew(Function() DoComputation(1.0)),
                            Task(Of Double).Factory.StartNew(Function() DoComputation(100.0)), 
                            Task(Of Double).Factory.StartNew(Function() DoComputation(1000.0)) }

        Dim results(taskArray.Length - 1) As Double
        Dim sum As Double
        
        For i As Integer = 0 To taskArray.Length - 1
            results(i) = taskArray(i).Result
            Console.Write("{0:N1} {1}", results(i), 
                              If(i = taskArray.Length - 1, "= ", "+ "))
            sum += results(i)
        Next
        Console.WriteLine("{0:N1}", sum)
   End Sub
   
   Private Function DoComputation(start As Double) As Double
      Dim sum As Double
      For value As Double = start To start + 10 Step .1
         sum += value
      Next
      Return sum 
   End Function
End Module
' The example displays the following output:
'       606.0 + 10,605.0 + 100,495.0 = 111,706.0

Weitere Informationen finden Sie unter Vorgehensweise: Zurückgeben eines Werts aus einer Aufgabe.For more information, see How to: Return a Value from a Task.

Wenn Sie einen Lambdaausdruck verwenden, um einen Delegaten zu erstellen, haben Sie Zugriff auf alle Variablen, die an dieser Stelle im Quellcode sichtbar sind.When you use a lambda expression to create a delegate, you have access to all the variables that are visible at that point in your source code. In einigen Fällen jedoch, insbesondere in Schleifen, wird die Variable nicht wie erwartet vom Lambda-Ausdruck erfasst.However, in some cases, most notably within loops, a lambda doesn't capture the variable as expected. Es wird nur der endgültige Wert erfasst, und nicht der nach jeder Iteration geänderte Wert.It only captures the final value, not the value as it mutates after each iteration. Das Problem wird anhand des folgenden Beispiels veranschaulicht.The following example illustrates the problem. Es wird ein Schleifenzähler an den Lambda-Ausdruck übergeben, wodurch ein CustomData-Objekt instanziiert wird und der Schleifenzähler als Objektbezeichner verwendet.It passes a loop counter to a lambda expression that instantiates a CustomData object and uses the loop counter as the object's identifier. Die Ausgabe im Beispiel zeigt, dass jedes CustomData-Objekt einen identischen Bezeichner hat.As the output from the example shows, each CustomData object has an identical identifier.

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

class CustomData
{
   public long CreationTime;
   public int Name; 
   public int ThreadNum;
}

public class Example
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in the loop
      // counter. This produces an unexpected result.
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj) => {
                                                 var data = new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks}; 
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                 Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum);
                                               },
                                              i );
      }
      Task.WaitAll(taskArray);     
   }
}
// The example displays output like the following:
//       Task #10 created at 635116418427727841 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427727841 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427737842 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

Class CustomData
   Public CreationTime As Long
   Public Name As Integer
   Public ThreadNum As Integer
End Class

Module Example
   Public Sub Main()
      ' Create the task object by using an Action(Of Object) to pass in the loop
      ' counter. This produces an unexpected result.
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} 
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                                 Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum)
                                              End Sub,
                                              i )
      Next
      Task.WaitAll(taskArray)     
   End Sub
End Module
' The example displays output like the following:
'       Task #10 created at 635116418427727841 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427737842 on thread #4.
'       Task #10 created at 635116418427727841 on thread #3.
'       Task #10 created at 635116418427747843 on thread #3.
'       Task #10 created at 635116418427747843 on thread #3.
'       Task #10 created at 635116418427737842 on thread #4.

Sie können auf den Wert jeder Iteration zugreifen, indem Sie für die Aufgabe über ihren Konstruktor ein Zustandsobjekt bereitstellen.You can access the value on each iteration by providing a state object to a task through its constructor. Im folgenden Beispiel wird das vorhergehende Beispiel geändert, indem der Schleifenzähler beim Erstellen des CustomData-Objekts verwendet wird, das wiederum an den Lambda-Ausdruck übergeben wird.The following example modifies the previous example by using the loop counter when creating the CustomData object, which, in turn, is passed to the lambda expression. Wie die Ausgabe im Beispiel zeigt, weist jedes CustomData-Objekt jetzt einen eindeutigen Bezeichner basierend auf den Wert des Schleifenzählers zum Zeitpunkt der Instanziierung des Objekts auf.As the output from the example shows, each CustomData object now has a unique identifier based on the value of the loop counter at the time the object was instantiated.

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

class CustomData
{
   public long CreationTime;
   public int Name; 
   public int ThreadNum;
}

public class Example
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in custom data
      // to the Task constructor. This is useful when you need to capture outer variables
      // from within a loop. 
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;
                                     
                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                  Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum);
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

Class CustomData
   Public CreationTime As Long
   Public Name As Integer
   Public ThreadNum As Integer
End Class

Module Example
   Public Sub Main()
      ' Create the task object by using an Action(Of Object) to pass in custom data
      ' to the Task constructor. This is useful when you need to capture outer variables
      ' from within a loop. 
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As CustomData = TryCast(obj, CustomData)
                                                 If data Is Nothing Then Return
                                     
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                                 Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, data.ThreadNum)
                                              End Sub,
                                              New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} )
      Next
      Task.WaitAll(taskArray)     
   End Sub
End Module
' The example displays output like the following:
'       Task #0 created at 635116412924597583 on thread #3.
'       Task #1 created at 635116412924607584 on thread #4.
'       Task #3 created at 635116412924607584 on thread #4.
'       Task #4 created at 635116412924607584 on thread #4.
'       Task #2 created at 635116412924607584 on thread #3.
'       Task #6 created at 635116412924607584 on thread #3.
'       Task #5 created at 635116412924607584 on thread #4.
'       Task #8 created at 635116412924607584 on thread #4.
'       Task #7 created at 635116412924607584 on thread #3.
'       Task #9 created at 635116412924607584 on thread #4.

Dieser Zustand wird als Argument an den Aufgabendelegaten übergeben, und mit der Task.AsyncState-Eigenschaft kann über das Aufgabenobjekt auf den Zustand zugegriffen werden.This state is passed as an argument to the task delegate, and it can be accessed from the task object by using the Task.AsyncState property. Das folgende Beispiel zeigt eine Abwandlung des vorherigen Beispiels.The following example is a variation on the previous example. Es verwendet die AsyncState-Eigenschaft, um Informationen zu den CustomData-Objekten anzuzeigen, die an den Lambdaausdruck übergeben wurden.It uses the AsyncState property to display information about the CustomData objects passed to the lambda expression.

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

class CustomData
{
   public long CreationTime;
   public int Name; 
   public int ThreadNum;
}

public class Example
{
   public static void Main()
   {
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null) 
                                                     return;
                                     
                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);     
      foreach (var task in taskArray) {
         var data = task.AsyncState as CustomData;
         if (data != null)
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum);
      }                     
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks

Class CustomData
   Public CreationTime As Long
   Public Name As Integer
   Public ThreadNum As Integer
End Class

Module Example
   Public Sub Main()
      Dim taskArray(9) As Task
      For i As Integer = 0 To taskArray.Length - 1
         taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                 Dim data As CustomData = TryCast(obj, CustomData)
                                                 If data Is Nothing Then Return
                                     
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId
                                              End Sub,
                                              New CustomData With {.Name = i, .CreationTime = DateTime.Now.Ticks} )
      Next
      Task.WaitAll(taskArray)     

      For Each task In taskArray
         Dim data = TryCast(task.AsyncState, CustomData)
         If data IsNot Nothing Then
            Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                              data.Name, data.CreationTime, data.ThreadNum)
         End If
      Next
   End Sub
End Module
' The example displays output like the following:
'    Task #0 created at 635116451245250515, ran on thread #3, RanToCompletion
'    Task #1 created at 635116451245270515, ran on thread #4, RanToCompletion
'    Task #2 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #3 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #4 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #5 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #6 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #7 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #8 created at 635116451245270515, ran on thread #3, RanToCompletion
'    Task #9 created at 635116451245270515, ran on thread #3, RanToCompletion

Aufgaben-IDTask ID

Jeder Aufgabe wird eine ganzzahlige ID zugeordnet, durch die diese in einer Anwendungsdomäne eindeutig identifiziert wird und auf die mit der Task.Id-Eigenschaft zugegriffen werden kann.Every task receives an integer ID that uniquely identifies it in an application domain and can be accessed by using the Task.Id property. Die ID vereinfacht das Anzeigen von Aufgabeninformationen in den Fenstern Parallele Stapel und Parallele Aufgaben des Visual Studio-Debuggers.The ID is useful for viewing task information in the Visual Studio debugger Parallel Stacks and Tasks windows. Die ID wird verzögert erzeugt, d. h., sie wird erst nach einer entsprechenden Anforderung erstellt. Eine Aufgabe kann daher bei jeder Programmausführung eine andere ID aufweisen.The ID is lazily created, which means that it isn't created until it is requested; therefore, a task may have a different ID every time the program is run. Weitere Informationen zum Anzeigen von Aufgaben-IDs im Debugger finden Sie unter Verwenden des Fensters „Aufgaben“ und Verwenden des Fensters „Parallele Stapel“.For more information about how to view task IDs in the debugger, see Using the Tasks Window and Using the Parallel Stacks Window.

AufgabenerstellungsoptionenTask creation options

Die meisten APIs, die Aufgaben erstellen, stellen Überladungen bereit, die einen TaskCreationOptions-Parameter akzeptieren.Most APIs that create tasks provide overloads that accept a TaskCreationOptions parameter. Durch Angabe einer dieser Optionen teilen Sie dem Aufgabenplaner mit, wie die Aufgabe im Threadpool geplant werden soll.By specifying one of these options, you tell the task scheduler how to schedule the task on the thread pool. In der folgenden Tabelle sind die verschiedenen Aufgabenerstellungsoptionen aufgeführt.The following table lists the various task creation options.

TaskCreationOptions-ParameterwertTaskCreationOptions parameter value BESCHREIBUNGDescription
None Dies ist die Standardoption, wenn keine Option angegeben wurde.The default when no option is specified. Der Planer verwendet zum Planen der Aufgabe seine Standardheuristik.The scheduler uses its default heuristics to schedule the task.
PreferFairness Gibt an, dass die Aufgabe so geplant werden soll, dass früher erstellte Aufgaben mit großer Wahrscheinlichkeit auch früher ausgeführt werden als Aufgaben, die später erstellt wurden.Specifies that the task should be scheduled so that tasks created sooner will be more likely to be executed sooner, and tasks created later will be more likely to execute later.
LongRunning Gibt an, dass die Aufgabe einen Vorgang mit langer Laufzeit darstellt.Specifies that the task represents a long-running operation.
AttachedToParent Gibt an, dass eine Aufgabe als angefügtes untergeordnetes Element der aktuellen Aufgabe erstellt werden soll (sofern vorhanden).Specifies that a task should be created as an attached child of the current task, if one exists. Weitere Informationen finden Sie unter Angefügte und getrennte untergeordnete Aufgaben.For more information, see Attached and Detached Child Tasks.
DenyChildAttach Gibt an, dass beim Angeben der AttachedToParent-Option durch eine innere Aufgabe diese Aufgabe nicht zu einer angefügten untergeordneten Aufgabe wird.Specifies that if an inner task specifies the AttachedToParent option, that task will not become an attached child task.
HideScheduler Gibt an, dass der Taskplaner für Aufgaben, die über das Aufrufen von Methoden wie TaskFactory.StartNew oder Task<TResult>.ContinueWith aus einer bestimmten Aufgabe erstellt werden, der Standardplaner ist und nicht der Planer, mit dem diese Aufgabe ausgeführt wird.Specifies that the task scheduler for tasks created by calling methods like TaskFactory.StartNew or Task<TResult>.ContinueWith from within a particular task is the default scheduler instead of the scheduler on which this task is running.

Die Optionen können mit einer bitweisen OR-Operation kombiniert werden.The options may be combined by using a bitwise OR operation. Im folgenden Beispiel wird eine Aufgabe veranschaulicht, die über die LongRunning-Option und die PreferFairness-Option verfügt.The following example shows a task that has the LongRunning and PreferFairness option.

var task3 = new Task(() => MyLongRunningMethod(),
                    TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();

Dim task3 = New Task(Sub() MyLongRunningMethod(),
                        TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()

Aufgaben, Threads und KulturTasks, threads, and culture

Jeder Thread hat eine zugeordnete Kultur und Benutzeroberflächenkultur, die durch die Thread.CurrentCulture- bzw. die Thread.CurrentUICulture-Eigenschaft definiert ist.Each thread has an associated culture and UI culture, which is defined by the Thread.CurrentCulture and Thread.CurrentUICulture properties, respectively. Die Kultur eines Threads wird in Vorgängen wie Formatieren, Analysieren, Sortieren und Zeichenfolgenvergleich verwendet.A thread's culture is used in such operations as formatting, parsing, sorting, and string comparison. Die Benutzeroberflächenkultur eines Threads wird für Nachschlagen in Ressourcen verwendet.A thread's UI culture is used in resource lookup. Normalerweise sind die Standardkultur und -benutzeroberflächenkultur eines Threads durch die Systemkultur definiert, es sei denn, Sie legen eine Standardkultur für alle Threads in einer Anwendungsdomäne mit der CultureInfo.DefaultThreadCurrentCulture- und der CultureInfo.DefaultThreadCurrentUICulture-Eigenschaft fest.Ordinarily, unless you specify a default culture for all the threads in an application domain by using the CultureInfo.DefaultThreadCurrentCulture and CultureInfo.DefaultThreadCurrentUICulture properties, the default culture and UI culture of a thread is defined by the system culture. Wenn Sie die Kultur eines Threads explizit festlegen und einen neuen Thread starten, erbt der neue Thread nicht die Kultur des aufrufenden Threads, sondern die Standardkultur des Systems wird als seine Kultur verwendet.If you explicitly set a thread's culture and launch a new thread, the new thread does not inherit the culture of the calling thread; instead, its culture is the default system culture. Im aufgabenbasierten Programmiermodell für Anwendungen, die gezielt Versionen von .NET Framework vor .NET Framework 4.6 verwenden, wird so vorgegangen.The task-based programming model for apps that target versions of the .NET Framework prior to .NET Framework 4.6 adhere to this practice.

Wichtig

Beachten Sie, dass die Kultur des aufrufenden Threads (als Bestandteil des Kontexts einer Aufgabe) für Anwendungen gilt, die gezielt .NET Framework 4.6 verwenden, und nicht für Anwendungen, deren Ausführung unter .NET Framework 4.6 erfolgt.Note that the calling thread's culture as part of a task's context applies to apps that target the .NET Framework 4.6, not apps that run under the .NET Framework 4.6. Sie können beim Erstellen Ihres Projekts in Visual Studio eine bestimmte Version von .NET Framework als Ziel angeben, indem Sie diese Version aus der Dropdownliste am oberen Rand des Dialogfelds Neues Projekt auswählen; außerhalb von Visual Studio können Sie das TargetFrameworkAttribute-Attribut verwenden.You can target a particular version of the .NET Framework when you create your project in Visual Studio by selecting that version from the dropdown list at the top of the New Project dialog box, or outside of Visual Studio you can use the TargetFrameworkAttribute attribute. Bei Anwendungen, die gezielt Versionen von .NET Framework vor .NET Framework 4.6 verwenden oder auf keine bestimmte Version von .NET Framework ausgerichtet sind, wird die Kultur einer Aufgabe weiterhin durch die Kultur des Threads bestimmt, über den sie ausgeführt werden.For apps that target versions of the .NET Framework prior to the .NET Framework 4.6, or that do not target a specific version of the .NET Framework, a task's culture continues to be determined by the culture of the thread on which it runs.

Beginnend mit Anwendungen, die gezielt .NET Framework 4.6 verwenden, erbt jede Aufgabe die Kultur des aufrufenden Threads. Das gilt selbst dann, wenn die Aufgabe asynchron über einen Threadpoolthread ausgeführt wird.Starting with apps that target the .NET Framework 4.6, the calling thread's culture is inherited by each task, even if the task runs asynchronously on a thread pool thread.

Das folgende Beispiel bietet eine einfache Veranschaulichung.The following example provides a simple illustration. In dem Beispiel wird das Attribut TargetFrameworkAttribute verwendet, um gezielt .NET Framework 4.6 zu verwenden, und die aktuelle Kultur der Anwendung wird entweder in Französisch (Frankreich) oder, wenn Französisch (Frankreich) bereits die aktuelle Kultur ist, in Englisch (USA) geändert.It uses the TargetFrameworkAttribute attribute to target the .NET Framework 4.6 and changes the app's current culture to either French (France) or, if French (France) is already the current culture, English (United States). Anschließend wird der Delegat formatDelegate aufgerufen, der einige Zahlen zurückgibt, die als Währungswerte in der neuen Kultur formatiert sind.It then invokes a delegate named formatDelegate that returns some numbers formatted as currency values in the new culture. Beachten Sie, dass der Delegat unabhängig davon, ob er als Aufgabe synchron oder asynchron ausgeführt wird, das erwartete Ergebnis zurückgibt, weil die asynchrone Aufgabe die Kultur des aufrufenden Threads erbt.Note that whether the delegate as a task either synchronously or asynchronously, it returns the expected result because the culture of the calling thread is inherited by the asynchronous task.

using System;
using System.Globalization;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;

[assembly:TargetFramework(".NETFramework,Version=v4.6")]

public class Example
{
   
   public static void Main()
   {
       decimal[] values = { 163025412.32m, 18905365.59m };
       string formatString = "C2";
       Func<String> formatDelegate = () => { string output = String.Format("Formatting using the {0} culture on thread {1}.\n",
                                                                           CultureInfo.CurrentCulture.Name,
                                                                           Thread.CurrentThread.ManagedThreadId);
                                             foreach (var value in values)
                                                output += String.Format("{0}   ", value.ToString(formatString));
                                                   
                                             output += Environment.NewLine;
                                             return output;
                                           };
       
       Console.WriteLine("The example is running on thread {0}", 
                         Thread.CurrentThread.ManagedThreadId);
       // Make the current culture different from the system culture.
       Console.WriteLine("The current culture is {0}", 
                         CultureInfo.CurrentCulture.Name);
       if (CultureInfo.CurrentCulture.Name == "fr-FR")
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
       else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

       Console.WriteLine("Changed the current culture to {0}.\n",
                         CultureInfo.CurrentCulture.Name);
       
       // Execute the delegate synchronously.
       Console.WriteLine("Executing the delegate synchronously:");
       Console.WriteLine(formatDelegate());
       
       // Call an async delegate to format the values using one format string.
       Console.WriteLine("Executing a task asynchronously:"); 
       var t1 = Task.Run(formatDelegate);
       Console.WriteLine(t1.Result);
       
       Console.WriteLine("Executing a task synchronously:");
       var t2 = new Task<String>(formatDelegate); 
       t2.RunSynchronously();
       Console.WriteLine(t2.Result);
   }
}
// The example displays the following output:
//         The example is running on thread 1
//         The current culture is en-US
//         Changed the current culture to fr-FR.
//
//         Executing the delegate synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task asynchronously:
//         Formatting using the fr-FR culture on thread 3.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
// If the TargetFrameworkAttribute statement is removed, the example
// displays the following output:
//          The example is running on thread 1
//          The current culture is en-US
//          Changed the current culture to fr-FR.
//
//          Executing the delegate synchronously:
//          Formatting using the fr-FR culture on thread 1.
//          163 025 412,32 €   18 905 365,59 €
//
//          Executing a task asynchronously:
//          Formatting using the en-US culture on thread 3.
//          $163,025,412.32   $18,905,365.59
//
//          Executing a task synchronously:
//          Formatting using the fr-FR culture on thread 1.
//          163 025 412,32 €   18 905 365,59 €
Imports System.Globalization
Imports System.Runtime.Versioning
Imports System.Threading
Imports System.Threading.Tasks

<Assembly:TargetFramework(".NETFramework,Version=v4.6")>

Module Example
   Public Sub Main()
       Dim values() As Decimal = { 163025412.32d, 18905365.59d }
       Dim formatString As String = "C2"
       Dim formatDelegate As Func(Of String) = Function()
                                                  Dim output As String = String.Format("Formatting using the {0} culture on thread {1}.",
                                                                                       CultureInfo.CurrentCulture.Name,
                                                                                       Thread.CurrentThread.ManagedThreadId)
                                                  output += Environment.NewLine
                                                  For Each value In values
                                                     output += String.Format("{0}   ", value.ToString(formatString))
                                                  Next 
                                                  output += Environment.NewLine
                                                  Return output
                                               End Function
       
       Console.WriteLine("The example is running on thread {0}", 
                         Thread.CurrentThread.ManagedThreadId)
       ' Make the current culture different from the system culture.
       Console.WriteLine("The current culture is {0}", 
                         CultureInfo.CurrentCulture.Name)
       If CultureInfo.CurrentCulture.Name = "fr-FR" Then
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US")
       Else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR")
       End If
       Console.WriteLine("Changed the current culture to {0}.",
                         CultureInfo.CurrentCulture.Name)
       Console.WriteLine()                  
       
       ' Execute the delegate synchronously.
       Console.WriteLine("Executing the delegate synchronously:")
       Console.WriteLine(formatDelegate())
       
       ' Call an async delegate to format the values using one format string.
       Console.WriteLine("Executing a task asynchronously:") 
       Dim t1 = Task.Run(formatDelegate)
       Console.WriteLine(t1.Result)
       
       Console.WriteLine("Executing a task synchronously:")
       Dim t2 = New Task(Of String)(formatDelegate) 
       t2.RunSynchronously()
       Console.WriteLine(t2.Result)
   End Sub
End Module
' The example displays the following output:
'          The example is running on thread 1
'          The current culture is en-US
'          Changed the current culture to fr-FR.
'
'          Executing the delegate synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task asynchronously:
'          Formatting Imports the fr-FR culture on thread 3.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
' If the TargetFrameworkAttribute statement is removed, the example
' displays the following output:
'          The example is running on thread 1
'          The current culture is en-US
'          Changed the current culture to fr-FR.
'
'          Executing the delegate synchronously:
'          Formatting using the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task asynchronously:
'          Formatting using the en-US culture on thread 3.
'          $163,025,412.32   $18,905,365.59
'
'          Executing a task synchronously:
'          Formatting using the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €

Wenn Sie Visual Studio verwenden, können Sie das TargetFrameworkAttribute-Attribut weglassen und stattdessen beim Erstellen des Projekts im Dialogfeld Neues Projekt .NET Framework 4.6 als Ziel auswählen.If you are using Visual Studio, you can omit the TargetFrameworkAttribute attribute and instead select the .NET Framework 4.6 as the target when you create the project in the New Project dialog.

Wenn die Ausgabe das Verhalten von Apps widerspiegeln soll, die gezielt .NET Framework-Versionen vor .NET Framework 4.6 verwenden, entfernen Sie das Attribut TargetFrameworkAttribute aus dem Quellcode.For output that reflects the behavior of apps the target versions of the .NET Framework prior to .NET Framework 4.6, remove the TargetFrameworkAttribute attribute from the source code. Die Ausgabe entspricht den Formatierungskonventionen der Standardsystemkultur, nicht der Kultur des aufrufenden Threads.The output will reflect the formatting conventions of the default system culture, not the culture of the calling thread.

Weitere Informationen zu asynchronen Aufgaben und Kultur, finden Sie im Thema CultureInfo im Abschnitt "Kultur und asynchrone aufgabenbasierte Vorgänge".For more information on asynchronous tasks and culture, see the "Culture and asynchronous task-based operations" section in the CultureInfo topic.

Erstellen von AufgabenfortsetzungenCreating task continuations

Mithilfe der Methoden Task.ContinueWith und Task<TResult>.ContinueWith können Sie eine Aufgabe angeben, die gestartet werden soll, wenn die Vorgängeraufgabe abgeschlossen wurde.The Task.ContinueWith and Task<TResult>.ContinueWith methods let you specify a task to start when the antecedent task finishes. An den Delegaten der Fortsetzungsaufgabe wird ein Verweis auf die vorherige Aufgabe übergeben, damit dieser den Status der vorherigen Aufgabe überprüfen kann und durch Abruf des Werts der Task<TResult>.Result-Eigenschaft die Ausgabe der vorherigen Aufgabe als Eingabe für die Fortsetzung verwenden kann.The delegate of the continuation task is passed a reference to the antecedent task so that it can examine the antecedent task's status and, by retrieving the value of the Task<TResult>.Result property, can use the output of the antecedent as input for the continuation.

Im folgenden Beispiel wird die getData-Aufgabe durch einen Aufruf der TaskFactory.StartNew<TResult>(Func<TResult>)-Methode gestartet.In the following example, the getData task is started by a call to the TaskFactory.StartNew<TResult>(Func<TResult>) method. Die processData-Aufgabe wird automatisch gestartet, wenn getData endet, und displayData wird gestartet, wenn processData endet.The processData task is started automatically when getData finishes, and displayData is started when processData finishes. getData erzeugt ein Ganzzahlarray, auf das über die processData-Aufgabe durch die getData-Eigenschaft der Task<TResult>.Result-Aufgabe zugegriffen werden kann.getData produces an integer array, which is accessible to the processData task through the getData task's Task<TResult>.Result property. Die processData Aufgabe verarbeitet das Array und gibt ein Ergebnis zurück, dessen Typ vom Rückgabetyp des Lambda-Ausdrucks abgeleitet wird, der an die Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>)-Methode übergeben wurde.The processData task processes that array and returns a result whose type is inferred from the return type of the lambda expression passed to the Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) method. Die displayData-Aufgabe wird automatisch ausgeführt, wenn processData endet und das Tuple<T1,T2,T3>-Objekt, das durch den processData-Lambda-Ausdruck zurückgegeben wurde, über die displayData-Eigenschaft der processData-Aufgabe für die Task<TResult>.Result-Aufgabe zugänglich ist.The displayData task executes automatically when processData finishes, and the Tuple<T1,T2,T3> object returned by the processData lambda expression is accessible to the displayData task through the processData task's Task<TResult>.Result property. Die displayData-Aufgabe nutzt das Ergebnis der processData-Aufgabe und erzeugt ein Ergebnis, dessen Typ auf ähnliche Weise abgeleitet wird und das dem Programm in der Result-Eigenschaft zur Verfügung gestellt wird.The displayData task takes the result of the processData task and produces a result whose type is inferred in a similar manner and which is made available to the program in the Result property.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {                         
      var getData = Task.Factory.StartNew(() => { 
                                             Random rnd = new Random(); 
                                             int[] values = new int[100];
                                             for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                values[ctr] = rnd.Next();

                                             return values;
                                          } );  
      var processData = getData.ContinueWith((x) => {
                                                int n = x.Result.Length;
                                                long sum = 0;
                                                double mean;
                                  
                                                for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                                   sum += x.Result[ctr];

                                                mean = sum / (double) n;
                                                return Tuple.Create(n, sum, mean);
                                             } ); 
      var displayData = processData.ContinueWith((x) => {
                                                    return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                         x.Result.Item1, x.Result.Item2, 
                                                                         x.Result.Item3);
                                                 } );                         
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim getData = Task.Factory.StartNew(Function() 
                                             Dim rnd As New Random()
                                             Dim values(99) As Integer
                                             For ctr = 0 To values.GetUpperBound(0)
                                                values(ctr) = rnd.Next()
                                             Next
                                             Return values
                                          End Function)  
      Dim processData = getData.ContinueWith(Function(x)
                                                Dim n As Integer = x.Result.Length
                                                Dim sum As Long
                                                Dim mean As Double
                                  
                                                For ctr = 0 To x.Result.GetUpperBound(0)
                                                   sum += x.Result(ctr)
                                                Next
                                                mean = sum / n
                                                Return Tuple.Create(n, sum, mean)
                                             End Function) 
      Dim displayData = processData.ContinueWith(Function(x)
                                                    Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                         x.Result.Item1, x.Result.Item2, 
                                                                         x.Result.Item3)
                                                 End Function)                         
      Console.WriteLine(displayData.Result)
   End Sub                        
End Module
' The example displays output like the following:
'   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82

Da Task.ContinueWith eine Instanzmethode ist, können Sie die Methodenaufrufe verketten, anstatt ein Task<TResult>-Objekt für jede Vorgängeraufgabe zu instanziieren.Because Task.ContinueWith is an instance method, you can chain method calls together instead of instantiating a Task<TResult> object for each antecedent task. Das folgende Beispiel entspricht funktionell dem vorherigen Beispiel, verkettet aber die Aufrufe der Task.ContinueWith-Methode.The following example is functionally identical to the previous example, except that it chains together calls to the Task.ContinueWith method. Beachten Sie, dass das Task<TResult>-Objekt, das durch die Kette von Methodenaufrufen zurückgegeben wird, die endgültige Fortsetzungsaufgabe ist.Note that the Task<TResult> object returned by the chain of method calls is the final continuation task.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {                         
      var displayData = Task.Factory.StartNew(() => { 
                                                 Random rnd = new Random(); 
                                                 int[] values = new int[100];
                                                 for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                    values[ctr] = rnd.Next();

                                                 return values;
                                              } ).  
                        ContinueWith((x) => {
                                        int n = x.Result.Length;
                                        long sum = 0;
                                        double mean;
                                  
                                        for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                           sum += x.Result[ctr];

                                        mean = sum / (double) n;
                                        return Tuple.Create(n, sum, mean);
                                     } ). 
                        ContinueWith((x) => {
                                        return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                             x.Result.Item1, x.Result.Item2, 
                                                             x.Result.Item3);
                                     } );                         
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim displayData = Task.Factory.StartNew(Function() 
                                                 Dim rnd As New Random()
                                                 Dim values(99) As Integer
                                                 For ctr = 0 To values.GetUpperBound(0)
                                                    values(ctr) = rnd.Next()
                                                 Next
                                                 Return values
                                              End Function). _  
                        ContinueWith(Function(x)
                                        Dim n As Integer = x.Result.Length
                                        Dim sum As Long
                                        Dim mean As Double
                                  
                                        For ctr = 0 To x.Result.GetUpperBound(0)
                                           sum += x.Result(ctr)
                                        Next
                                        mean = sum / n
                                        Return Tuple.Create(n, sum, mean)
                                     End Function). _ 
                        ContinueWith(Function(x)
                                        Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                             x.Result.Item1, x.Result.Item2,  
                                                             x.Result.Item3)
                                     End Function)                         
      Console.WriteLine(displayData.Result)
   End Sub                        
End Module
' The example displays output like the following:
'   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82

Die ContinueWhenAll-Methode und die ContinueWhenAny-Methode ermöglichen es Ihnen, die Ausführung von mehreren Aufgaben fortzusetzen.The ContinueWhenAll and ContinueWhenAny methods enable you to continue from multiple tasks.

Weitere Informationen finden Sie unter Verketten von Aufgaben mithilfe von Fortsetzungsaufgaben.For more information, see Chaining Tasks by Using Continuation Tasks.

Erstellen von getrennten untergeordneten AufgabenCreating detached child tasks

Wenn durch Benutzercode, der in einer Aufgabe ausgeführt wird, eine neue Aufgabe ohne Angabe der AttachedToParent-Option erstellt wird, ist die neue Aufgabe auf keine besondere Weise mit der übergeordneten Aufgabe synchronisiert.When user code that is running in a task creates a new task and does not specify the AttachedToParent option, the new task is not synchronized with the parent task in any special way. Dieser Typ einer nicht synchronisierten Aufgabe wird als getrennte geschachtelte Aufgabe oder getrennte untergeordnete Aufgabe bezeichnet.This type of non-synchronized task is called a detached nested task or detached child task. Im folgenden Beispiel wird eine Aufgabe dargestellt, die eine getrennte untergeordnete Aufgabe erstellt.The following example shows a task that creates one detached child task.

var outer = Task.Factory.StartNew(() =>
{
    Console.WriteLine("Outer task beginning.");

    var child = Task.Factory.StartNew(() =>
    {
        Thread.SpinWait(5000000);
        Console.WriteLine("Detached task completed.");
    });

});

outer.Wait();
Console.WriteLine("Outer task completed.");
// The example displays the following output:
//    Outer task beginning.
//    Outer task completed.
//    Detached task completed.
Dim outer = Task.Factory.StartNew(Sub()
                                      Console.WriteLine("Outer task beginning.")
                                      Dim child = Task.Factory.StartNew(Sub()
                                                                            Thread.SpinWait(5000000)
                                                                            Console.WriteLine("Detached task completed.")
                                                                        End Sub)
                                  End Sub)
outer.Wait()
Console.WriteLine("Outer task completed.")
' The example displays the following output:
'     Outer task beginning.
'     Outer task completed.
'    Detached child completed.

Beachten Sie, dass die übergeordnete Aufgabe nicht auf den Abschluss der getrennten untergeordneten Aufgabe wartet.Note that the parent task does not wait for the detached child task to finish.

Erstellen von untergeordneten AufgabenCreating child tasks

Wenn durch Benutzercode, der in einer Aufgabe ausgeführt wird, eine Aufgabe mit der AttachedToParent-Option erstellt wird, wird die neue Aufgabe als angefügte untergeordnete Aufgabe der übergeordneten Aufgabe bezeichnet.When user code that is running in a task creates a task with the AttachedToParent option, the new task is known as a attached child task of the parent task. Mithilfe der AttachedToParent-Option können Sie eine strukturierte Aufgabenparallelität angeben, da die übergeordnete Aufgabe implizit auf den Abschluss aller angefügten untergeordneten Aufgaben wartet.You can use the AttachedToParent option to express structured task parallelism, because the parent task implicitly waits for all attached child tasks to finish. Im folgenden Beispiel wird eine übergeordnete Aufgabe dargestellt, die zehn angefügte untergeordnete Aufgaben erstellt.The following example shows a parent task that creates ten attached child tasks. Beachten Sie, dass, obwohl das Beispiel die Task.Wait-Methode aufruft, um auf den Abschluss der übergeordneten Aufgabe zu warten, nicht explizit auf den Abschluss der angefügten untergeordneten Aufgabe gewartet werden muss.Note that although the example calls the Task.Wait method to wait for the parent task to finish, it does not have to explicitly wait for the attached child tasks to complete.

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

public class Example
{
   public static void Main()
   {
      var parent = Task.Factory.StartNew(() => {
                      Console.WriteLine("Parent task beginning.");
                      for (int ctr = 0; ctr < 10; ctr++) {
                         int taskNo = ctr;
                         Task.Factory.StartNew((x) => {
                                                  Thread.SpinWait(5000000);
                                                  Console.WriteLine("Attached child #{0} completed.", 
                                                                    x);
                                               },
                                               taskNo, TaskCreationOptions.AttachedToParent);
                      }
                   });

      parent.Wait();
      Console.WriteLine("Parent task completed.");
   }
}
// The example displays output like the following:
//       Parent task beginning.
//       Attached child #9 completed.
//       Attached child #0 completed.
//       Attached child #8 completed.
//       Attached child #1 completed.
//       Attached child #7 completed.
//       Attached child #2 completed.
//       Attached child #6 completed.
//       Attached child #3 completed.
//       Attached child #5 completed.
//       Attached child #4 completed.
//       Parent task completed.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim parent = Task.Factory.StartNew(Sub()
                                            Console.WriteLine("Parent task beginning.")
                                            For ctr As Integer = 0 To 9
                                               Dim taskNo As Integer = ctr
                                               Task.Factory.StartNew(Sub(x)
                                                                        Thread.SpinWait(5000000)
                                                                        Console.WriteLine("Attached child #{0} completed.", 
                                                                                          x)
                                                                     End Sub,
                                                                     taskNo, TaskCreationOptions.AttachedToParent)
                                            Next
                                         End Sub)
      parent.Wait()
      Console.WriteLine("Parent task completed.")
   End Sub
End Module   
' The example displays output like the following:
'       Parent task beginning.
'       Attached child #9 completed.
'       Attached child #0 completed.
'       Attached child #8 completed.
'       Attached child #1 completed.
'       Attached child #7 completed.
'       Attached child #2 completed.
'       Attached child #6 completed.
'       Attached child #3 completed.
'       Attached child #5 completed.
'       Attached child #4 completed.
'       Parent task completed.

Durch das Angeben der TaskCreationOptions.DenyChildAttach-Option für eine übergeordnete Aufgabe kann das Anfügen anderer Aufgaben an die übergeordnete Aufgabe verhindert werden.A parent task can use the TaskCreationOptions.DenyChildAttach option to prevent other tasks from attaching to the parent task. Weitere Informationen finden Sie unter Angefügte und getrennte untergeordnete Aufgaben.For more information, see Attached and Detached Child Tasks.

Warten auf die Beendigung von AufgabenWaiting for tasks to finish

Der System.Threading.Tasks.Task-Typ und der System.Threading.Tasks.Task<TResult>-Typ bieten mehrere Überladungen der Task.Wait-Methode, die das Warten auf den Abschluss einer Aufgabe ermöglichen.The System.Threading.Tasks.Task and System.Threading.Tasks.Task<TResult> types provide several overloads of the Task.Wait methods that enable you to wait for a task to finish. Außerdem können Sie mittels Überladungen der statischen Task.WaitAll-Methode und der Task.WaitAny-Methode auf den Abschluss einer bestimmten oder aller Aufgaben in einem Array warten.In addition, overloads of the static Task.WaitAll and Task.WaitAny methods let you wait for any or all of an array of tasks to finish.

In der Regel wird aus einem der folgenden Gründe auf den Abschluss einer Aufgabe gewartet:Typically, you would wait for a task for one of these reasons:

  • Der Hauptthread hängt von dem Endergebnis ab, das von einer Aufgabe berechnet wird.The main thread depends on the final result computed by a task.

  • Sie müssen Ausnahmen behandeln, die möglicherweise von der Aufgabe ausgelöst werden.You have to handle exceptions that might be thrown from the task.

  • Die Anwendung wird möglicherweise beendet, ehe die Ausführung aller Aufgaben abgeschlossen ist.The application may terminate before all tasks have completed execution. Beispielsweise werden Konsolenanwendungen beendet, sobald der synchrone Code in Main (dem Anwendungseinstiegspunkt) ausgeführt wurde.For example, console applications will terminate as soon as all synchronous code in Main (the application entry point) has executed.

Im folgenden Beispiel wird das grundlegende Muster dargestellt, das keine Ausnahmebehandlung beinhaltet.The following example shows the basic pattern that does not involve exception handling.

Task[] tasks = new Task[3]
{
    Task.Factory.StartNew(() => MethodA()),
    Task.Factory.StartNew(() => MethodB()),
    Task.Factory.StartNew(() => MethodC())
};

//Block until all tasks complete.
Task.WaitAll(tasks);

// Continue on this thread...
Dim tasks() =
{
    Task.Factory.StartNew(Sub() MethodA()),
    Task.Factory.StartNew(Sub() MethodB()),
    Task.Factory.StartNew(Sub() MethodC())
}

' Block until all tasks complete.
Task.WaitAll(tasks)

' Continue on this thread...

Ein Beispiel, in dem die Behandlung von Ausnahmen veranschaulicht wird, finden Sie unter Ausnahmebehandlung.For an example that shows exception handling, see Exception Handling.

Bei einigen Überladungen können Sie einen Timeout angeben, während andere ein zusätzliches CancellationToken als Eingabeparameter nutzen, sodass der Wartevorgang selbst entweder programmgesteuert oder als Reaktion auf eine Benutzereingabe abgebrochen werden kann.Some overloads let you specify a time-out, and others take an additional CancellationToken as an input parameter, so that the wait itself can be canceled either programmatically or in response to user input.

Beim Warten auf eine Aufgabe wird implizit auf alle untergeordneten Elemente dieser Aufgabe gewartet, die mit der TaskCreationOptions.AttachedToParent-Option erstellt wurden.When you wait for a task, you implicitly wait for all children of that task that were created by using the TaskCreationOptions.AttachedToParent option. Task.Wait wird sofort zurückgegeben, wenn die Aufgabe bereits abgeschlossen wurde.Task.Wait returns immediately if the task has already completed. Alle von einer Aufgabe ausgelöste Ausnahmen werden von einer Task.Wait-Methode ausgelöst, auch wenn die Task.Wait-Methode nach Abschluss der Aufgabe aufgerufen wurde.Any exceptions raised by a task will be thrown by a Task.Wait method, even if the Task.Wait method was called after the task completed.

Verfassen von AufgabenComposing tasks

Die Klassen Task und Task<TResult> bieten mehrere Methoden zur einfacheren Erstellung mehrerer Aufgaben, um allgemeine Muster zu implementieren und die in C#, Visual Basic sowie F# bereitgestellten asynchronen Sprachfeatures besser einzusetzen.The Task and Task<TResult> classes provide several methods that can help you compose multiple tasks to implement common patterns and to better use the asynchronous language features that are provided by C#, Visual Basic, and F#. In diesem Abschnitt werden die Methoden WhenAll, WhenAny, Delay und FromResult beschrieben.This section describes the WhenAll, WhenAny, Delay, and FromResult methods.

Task.WhenAllTask.WhenAll

Die Task.WhenAll-Methode wartet asynchron auf den Abschluss mehrerer Task-Objekte oder Task<TResult>-Objekte.The Task.WhenAll method asynchronously waits for multiple Task or Task<TResult> objects to finish. Die Methode stellt überladene Versionen zum Warten auf nicht einheitliche Sätze von Aufgaben bereit.It provides overloaded versions that enable you to wait for non-uniform sets of tasks. Beispielsweise kann in einem Methodenaufruf auf den Abschluss mehrerer Task-Objekte und Task<TResult>-Objekte gewartet werden.For example, you can wait for multiple Task and Task<TResult> objects to complete from one method call.

Task.WhenAnyTask.WhenAny

Die Task.WhenAny-Methode wartet asynchron auf den Abschluss eines von mehreren Task-Objekten oder Task<TResult>-Objekten.The Task.WhenAny method asynchronously waits for one of multiple Task or Task<TResult> objects to finish. Wie die Task.WhenAll-Methode stellt auch diese Methode überladene Versionen bereit, mit denen auf nicht einheitliche Sätze von Aufgaben gewartet werden kann.As in the Task.WhenAll method, this method provides overloaded versions that enable you to wait for non-uniform sets of tasks. Die WhenAny-Methode ist insbesondere in folgenden Szenarien nützlich.The WhenAny method is especially useful in the following scenarios.

  • Redundante Vorgänge.Redundant operations. Betrachten Sie einen Algorithmus oder einen Vorgang, der auf verschiedene Weise ausgeführt werden kann.Consider an algorithm or operation that can be performed in many ways. Sie können die WhenAny-Methode verwenden, um den Vorgang auszuwählen, der zuerst beendet wird, und dann die verbleibenden Vorgänge abzubrechen.You can use the WhenAny method to select the operation that finishes first and then cancel the remaining operations.

  • Überlappende Vorgänge.Interleaved operations. Sie können mehrere Vorgänge starten, die alle beendet werden müssen, und die WhenAny-Methode verwenden, um Ergebnisse zu verarbeiten, wenn jeder Vorgang beendet wird.You can start multiple operations that must all finish and use the WhenAny method to process results as each operation finishes. Nachdem ein Vorgang beendet wurde, können Sie eine oder mehrere weitere Aufgaben starten.After one operation finishes, you can start one or more additional tasks.

  • Eingeschränkte Vorgänge.Throttled operations. Sie können die WhenAny-Methode verwenden, um das vorherige Szenario zu erweitern, indem Sie die Anzahl der gleichzeitigen Vorgänge einschränken.You can use the WhenAny method to extend the previous scenario by limiting the number of concurrent operations.

  • Abgelaufene Vorgänge.Expired operations. Sie können die WhenAny-Methode verwenden, um eine Auswahl zwischen einer oder mehreren Aufgaben und einer anderen Aufgabe zu treffen, die nach einem bestimmten Zeitpunkt abgeschlossen wird, z. B. die von der Delay-Methode zurückgegebene Aufgabe.You can use the WhenAny method to select between one or more tasks and a task that finishes after a specific time, such as a task that is returned by the Delay method. Die Delay-Methode wird im folgenden Abschnitt beschrieben.The Delay method is described in the following section.

Task.DelayTask.Delay

Die Task.Delay-Methode generiert Task-Objekt, das nach dem angegebenen Zeitraum abgeschlossen wird.The Task.Delay method produces a Task object that finishes after the specified time. Mithilfe dieser Methode können Sie Schleifen erstellen, die gelegentlich Daten abrufen, Timeouts einfügen, die Verarbeitung von Benutzereingaben für einen vorab festgelegten Zeitraum verzögern usw.You can use this method to build loops that occasionally poll for data, introduce time-outs, delay the handling of user input for a predetermined time, and so on.

Task(T).FromResultTask(T).FromResult

Mit der Task.FromResult-Methode, können Sie ein Task<TResult>-Objekt erstellen, das ein vorberechnetes Ergebnis enthält.By using the Task.FromResult method, you can create a Task<TResult> object that holds a pre-computed result. Diese Methode ist nützlich, wenn Sie einen asynchronen Vorgang ausführen, der ein Task<TResult>-Objekt zurückgibt, und das Ergebnis dieses Task<TResult>-Objekts bereits berechnet wurde.This method is useful when you perform an asynchronous operation that returns a Task<TResult> object, and the result of that Task<TResult> object is already computed. Ein Beispiel, das FromResult verwendet, um die Ergebnisse asynchroner Downloadvorgänge aus einem Cache abzurufen, finden Sie unter Vorgehensweise: Erstellen von vorberechneten Aufgaben.For an example that uses FromResult to retrieve the results of asynchronous download operations that are held in a cache, see How to: Create Pre-Computed Tasks.

Behandeln von Ausnahmen in AufgabenHandling exceptions in tasks

Wenn eine Aufgabe eine oder mehrere Ausnahmen auslöst, werden die Ausnahmen in eine AggregateException-Ausnahme eingeschlossen.When a task throws one or more exceptions, the exceptions are wrapped in an AggregateException exception. Diese Ausnahme wird an den Thread zurückgegeben, der mit der Aufgabe verbunden ist. In der Regel ist dies der Thread, der auf den Abschluss der Aufgabe wartet oder der Thread, der auf die Result-Eigenschaft zugreift.That exception is propagated back to the thread that joins with the task, which is typically the thread that is waiting for the task to finish or the thread that accesses the Result property. Dieses Verhalten trägt dazu bei, dass die .NET Framework-Richtlinie umgesetzt wird, der zufolge alle Ausnahmefehler standardmäßig zu einem Beenden des Prozesses führen.This behavior serves to enforce the .NET Framework policy that all unhandled exceptions by default should terminate the process. Der aufrufende Code kann die Ausnahmen behandeln, indem er Folgendes in einem try/catch-Block verwendet:The calling code can handle the exceptions by using any of the following in a try/catch block:

Der Verbindungsthread kann Ausnahmen ebenfalls behandeln, indem er auf die Exception-Eigenschaft zugreift, bevor die Aufgabe der Garbage Collection zugeordnet wird.The joining thread can also handle exceptions by accessing the Exception property before the task is garbage-collected. Durch den Zugriff auf diese Eigenschaft verhindern Sie, dass der Ausnahmefehler das Ausnahmeweitergabe-Verhalten auslöst, durch das der Prozess beim Abschluss des Objekts beendet wird.By accessing this property, you prevent the unhandled exception from triggering the exception propagation behavior that terminates the process when the object is finalized.

Weitere Informationen zu Ausnahmen und Aufgaben finden Sie unter Ausnahmebehandlung.For more information about exceptions and tasks, see Exception Handling.

Abbrechen von AufgabenCanceling tasks

Die Task-Klasse unterstützt einen kooperativen Abbruch und ist vollständig in die System.Threading.CancellationTokenSource-Klasse und die System.Threading.CancellationToken-Klasse integriert, die in .NET Framework 4 eingeführt wurden.The Task class supports cooperative cancellation and is fully integrated with the System.Threading.CancellationTokenSource and System.Threading.CancellationToken classes, which were introduced in the .NET Framework 4. Viele Konstruktoren in der System.Threading.Tasks.Task-Klasse verwenden ein CancellationToken-Objekt als Eingabeparameter.Many of the constructors in the System.Threading.Tasks.Task class take a CancellationToken object as an input parameter. Viele der StartNew- und Run-Überladungen enthalten auch einen CancellationToken-Parameter.Many of the StartNew and Run overloads also include a CancellationToken parameter.

Mit der CancellationTokenSource-Klasse können Sie das Token erstellen und die Abbruchanforderung zu einem späteren Zeitpunkt ausgeben.You can create the token, and issue the cancellation request at some later time, by using the CancellationTokenSource class. Übergeben Sie das Token als Argument an Task, und verweisen Sie in dem Benutzerdelegaten, der auf eine Abbruchanforderung reagiert, auf das gleiche Token.Pass the token to the Task as an argument, and also reference the same token in your user delegate, which does the work of responding to a cancellation request.

Weitere Informationen finden Sie unter Aufgabenabbruch und Vorgehensweise: Abbrechen einer Aufgabe und ihrer untergeordneten Elemente.For more information, see Task Cancellation and How to: Cancel a Task and Its Children.

Die TaskFactory-KlasseThe TaskFactory class

Die TaskFactory-Klasse stellt statische Methoden bereit, die einige allgemeine Muster zum Erstellen und Starten von Aufgaben und Fortsetzungsaufgaben kapseln.The TaskFactory class provides static methods that encapsulate some common patterns for creating and starting tasks and continuation tasks.

Auf die standardmäßige TaskFactory kann als statische Eigenschaft in der Task-Klasse oder Task<TResult>-Klasse zugegriffen werden.The default TaskFactory can be accessed as a static property on the Task class or Task<TResult> class. Sie können eine TaskFactory auch direkt instanziieren und verschiedene Optionen angeben, einschließlich CancellationToken, TaskCreationOptions, TaskContinuationOptions oder TaskScheduler.You can also instantiate a TaskFactory directly and specify various options that include a CancellationToken, a TaskCreationOptions option, a TaskContinuationOptions option, or a TaskScheduler. Die beim Erstellen der Aufgabenfactory angegebenen Optionen werden auf alle Aufgaben angewendet, die von dieser erstellt werden, außer Sie erstellen Task mit der TaskCreationOptions-Enumeration. In diesem Fall werden die Optionen der Aufgabenfactory mit den Optionen der Aufgabe überschrieben.Whatever options are specified when you create the task factory will be applied to all tasks that it creates, unless the Task is created by using the TaskCreationOptions enumeration, in which case the task's options override those of the task factory.

Aufgaben ohne DelegatenTasks without delegates

In einigen Fällen können Sie mithilfe einer Task einen asynchronen Vorgang kapseln, der nicht von Ihrem Benutzerdelegaten, sondern von einer externen Komponente durchgeführt wird.In some cases, you may want to use a Task to encapsulate some asynchronous operation that is performed by an external component instead of your own user delegate. Wenn der Vorgang auf dem Begin/End-Muster des asynchronen Programmiermodells basiert, können Sie die FromAsync-Methoden verwenden.If the operation is based on the Asynchronous Programming Model Begin/End pattern, you can use the FromAsync methods. Wenn das nicht der Fall ist, können Sie den Vorgang mithilfe des TaskCompletionSource<TResult>-Objekts in eine Aufgabe einschließen und dadurch bestimmte Vorteile der Task-Programmierbarkeit erhalten, z. B. Unterstützung von Ausnahmeweitergabe und Fortsetzungen.If that is not the case, you can use the TaskCompletionSource<TResult> object to wrap the operation in a task and thereby gain some of the benefits of Task programmability, for example, support for exception propagation and continuations. Weitere Informationen finden Sie unter TaskCompletionSource<TResult>.For more information, see TaskCompletionSource<TResult>.

Benutzerdefinierte PlanerCustom schedulers

Die meisten Anwendungs- oder Bibliotheksentwickler machen sich keine Gedanken über den für die Ausführung der Aufgabe verwendeten Prozessor, über die Synchronisierung der Arbeit mit anderen Aufgaben oder die Planung im System.Threading.ThreadPool.Most application or library developers do not care which processor the task runs on, how it synchronizes its work with other tasks, or how it is scheduled on the System.Threading.ThreadPool. Die einzige Voraussetzung für sie ist eine möglichst effiziente Ausführung auf dem Hostcomputer.They only require that it execute as efficiently as possible on the host computer. Wenn Sie eine präzisere Steuerung der Planungsdetails benötigen, können Sie in der Task Parallel Library bestimmte Einstellungen im Standardaufgabenplaner konfigurieren und sogar einen benutzerdefinierten Planer angeben.If you require more fine-grained control over the scheduling details, the Task Parallel Library lets you configure some settings on the default task scheduler, and even lets you supply a custom scheduler. Weitere Informationen finden Sie unter TaskScheduler.For more information, see TaskScheduler.

Die TPL beinhaltet zahlreiche neue öffentliche Typen, die sowohl in parallelen, als auch in sequenziellen Szenarios hilfreich sind.The TPL has several new public types that are useful in both parallel and sequential scenarios. Hierzu zählen mehrere threadsichere, schnelle und skalierbare Auflistungsklassen im System.Collections.Concurrent-Namespace sowie mehrere neue Synchronisierungstypen, z. B. System.Threading.Semaphore und System.Threading.ManualResetEventSlim, die für bestimmte Arten von Arbeitslasten effizienter als ihre Vorgänger sind.These include several thread-safe, fast and scalable collection classes in the System.Collections.Concurrent namespace, and several new synchronization types, for example, System.Threading.Semaphore and System.Threading.ManualResetEventSlim, which are more efficient than their predecessors for specific kinds of workloads. Andere neue Typen in .NET Framework 4, z. B. System.Threading.Barrier und System.Threading.SpinLock, stellen Funktionalität bereit, die in früheren Releases nicht verfügbar war.Other new types in the .NET Framework 4, for example, System.Threading.Barrier and System.Threading.SpinLock, provide functionality that was not available in earlier releases. Weitere Informationen finden Sie unter Datenstrukturen für die parallele Programmierung.For more information, see Data Structures for Parallel Programming.

Benutzerdefinierte AufgabentypenCustom task types

Es wird empfohlen, nicht von System.Threading.Tasks.Task oder System.Threading.Tasks.Task<TResult> zu erben.We recommend that you do not inherit from System.Threading.Tasks.Task or System.Threading.Tasks.Task<TResult>. Stattdessen sollten Sie die AsyncState-Eigenschaft verwenden, um einem Task-Objekt oder einem Task<TResult>-Objekt zusätzliche Daten oder einen zusätzlichen Zustand zuzuordnen.Instead, we recommend that you use the AsyncState property to associate additional data or state with a Task or Task<TResult> object. Sie können auch Erweiterungsmethoden verwenden, um die Funktionen der Task-Klasse und der Task<TResult>-Klasse zu erweitern.You can also use extension methods to extend the functionality of the Task and Task<TResult> classes. Weitere Informationen zu Erweiterungsmethoden finden Sie unter Erweiterungsmethoden und Erweiterungsmethoden.For more information about extension methods, see Extension Methods and Extension Methods.

Wenn Sie von Task oder Task<TResult> erben müssen, können Sie weder Run und Run noch die Klassen System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult> oder System.Threading.Tasks.TaskCompletionSource<TResult> zum Erstellen von Instanzen des benutzerdefinierten Aufgabentyps verwenden, da von diesen Mechanismen nur Task-Objekte und Task<TResult>-Objekte erstellt werden.If you must inherit from Task or Task<TResult>, you cannot use Run, Run, or the System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>, or System.Threading.Tasks.TaskCompletionSource<TResult> classes to create instances of your custom task type because these mechanisms create only Task and Task<TResult> objects. Außerdem können Sie nicht die von Task, Task<TResult>, TaskFactory und TaskFactory<TResult> bereitgestellten Aufgabenfortsetzungsmechanismen verwenden, um Instanzen des benutzerdefinierten Aufgabentyps zu erstellen, da mit diesen Mechanismen ebenfalls nur Task-Objekte und Task<TResult>-Objekte erstellt werden.In addition, you cannot use the task continuation mechanisms that are provided by Task, Task<TResult>, TaskFactory, and TaskFactory<TResult> to create instances of your custom task type because these mechanisms also create only Task and Task<TResult> objects.

TitelTitle BESCHREIBUNGDescription
Verketten von Aufgaben mithilfe von FortsetzungsaufgabenChaining Tasks by Using Continuation Tasks Beschreibt die Funktionsweise von Fortsetzungen.Describes how continuations work.
Angefügte und getrennte untergeordnete AufgabenAttached and Detached Child Tasks Beschreibt den Unterschied zwischen angefügten und getrennten untergeordneten Aufgaben.Describes the difference between attached and detached child tasks.
AufgabenabbruchTask Cancellation Beschreibt die integrierte Abbruchunterstützung des Task-Objekts.Describes the cancellation support that is built into the Task object.
AusnahmebehandlungException Handling Beschreibt die Behandlung von Ausnahmen in gleichzeitigen Threads.Describes how exceptions on concurrent threads are handled.
Vorgehensweise: Ausführen von parallelen Vorgängen mithilfe von „Parallel.Invoke“How to: Use Parallel.Invoke to Execute Parallel Operations Beschreibt die Verwendung von Invoke.Describes how to use Invoke.
Vorgehensweise: Zurückgeben eines Werts aus einer AufgabeHow to: Return a Value from a Task Beschreibt, wie in Aufgaben Werte zurückgegeben werden.Describes how to return values from tasks.
Vorgehensweise: Abbrechen einer Aufgabe und ihrer untergeordneten ElementeHow to: Cancel a Task and Its Children Beschreibt, wie Aufgaben abgebrochen werden.Describes how to cancel tasks.
Vorgehensweise: Erstellen von vorberechneten AufgabenHow to: Create Pre-Computed Tasks Beschreibt, wie mithilfe der Task.FromResult-Methode die Ergebnisse asynchroner Downloadvorgänge aus einem Cache abgerufen werden können.Describes how to use the Task.FromResult method to retrieve the results of asynchronous download operations that are held in a cache.
Vorgehensweise: Durchlaufen einer binären Struktur mit parallelen AufgabenHow to: Traverse a Binary Tree with Parallel Tasks Beschreibt, wie Aufgaben zum Traversieren einer binären Struktur verwendet werden.Describes how to use tasks to traverse a binary tree.
Vorgehensweise: Entpacken einer geschachtelten AufgabeHow to: Unwrap a Nested Task Veranschaulicht die Verwendung der Unwrap-Erweiterungsmethode.Demonstrates how to use the Unwrap extension method.
DatenparallelitätData Parallelism Beschreibt, wie Sie mithilfe von For und ForEach parallele Schleifen für Daten erstellen.Describes how to use For and ForEach to create parallel loops over data.
Parallele ProgrammierungParallel Programming Der Knoten auf oberster Ebene für die parallele .NET Framework-Programmierung.Top level node for .NET Framework parallel programming.

Siehe auchSee also