Asynchrone programmering op basis van taken

De TPL (Task Parallel Library) is gebaseerd op het concept van een taak, die een asynchrone bewerking vertegenwoordigt. Op sommige manieren lijkt een taak op een thread of ThreadPool werkitem, maar op een hoger abstractieniveau. De term taakparallelisme verwijst naar een of meer onafhankelijke taken die gelijktijdig worden uitgevoerd. Taken bieden twee primaire voordelen:

  • Efficiënter en schaalbaarder gebruik van systeemresources.

    Achter de schermen worden taken in de wachtrij geplaatst ThreadPool, die is uitgebreid met algoritmen die het aantal threads bepalen en aanpassen. Deze algoritmen bieden taakverdeling om de doorvoer te maximaliseren. Dit proces maakt taken relatief lichtgewicht en u kunt er veel maken om fijnmazige parallelle uitvoering mogelijk te maken.

  • Meer programmatisch beheer dan mogelijk is met een thread of werkitem.

    Taken en het framework dat er omheen is gebouwd, bieden een uitgebreide set API's die ondersteuning bieden voor wachten, annuleren, vervolgen, robuuste afhandeling van uitzonderingen, gedetailleerde status, aangepaste planning en meer.

Om beide redenen is TPL de voorkeurs-API voor het schrijven van multithreaded, asynchrone en parallelle code in .NET.

Impliciet taken maken en uitvoeren

De Parallel.Invoke methode biedt een handige manier om een willekeurig aantal willekeurige instructies gelijktijdig uit te voeren. Geef een Action gemachtigde door voor elk werkitem. De eenvoudigste manier om deze gemachtigden te maken, is door lambda-expressies te gebruiken. De lambda-expressie kan een benoemde methode aanroepen of de code inline opgeven. In het volgende voorbeeld ziet u een eenvoudige Invoke aanroep waarmee twee taken worden gemaakt en gestart die gelijktijdig worden uitgevoerd. De eerste taak wordt vertegenwoordigd door een lambda-expressie die een methode aanroept met de naam DoSomeWorken de tweede taak wordt vertegenwoordigd door een lambda-expressie die een methode aanroept met de naam DoSomeOtherWork.

Notitie

In deze documentatie worden lambda-expressies gebruikt om gedelegeerden in TPL te definiëren. Als u niet bekend bent met lambda-expressies in C# of Visual Basic, raadpleegt u Lambda-expressies in PLINQ en TPL.

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

Notitie

Het aantal Task instanties dat achter de schermen wordt gemaakt, Invoke is niet noodzakelijkerwijs gelijk aan het aantal gedelegeerden dat wordt opgegeven. De TPL kan verschillende optimalisaties gebruiken, met name met grote aantallen gemachtigden.

Zie Voor meer informatie: Parallel.Invoke gebruiken om parallelle bewerkingen uit te voeren.

Voor meer controle over taakuitvoering of om een waarde van de taak te retourneren, moet u expliciet met Task objecten werken.

Taken expliciet maken en uitvoeren

Een taak die geen waarde retourneert, wordt vertegenwoordigd door de System.Threading.Tasks.Task klasse. Een taak die een waarde retourneert, wordt vertegenwoordigd door de System.Threading.Tasks.Task<TResult> klasse, die van Task. Het taakobject verwerkt de infrastructuurdetails en biedt methoden en eigenschappen die toegankelijk zijn vanuit de aanroepende thread gedurende de levensduur van de taak. U kunt bijvoorbeeld op elk gewenst moment toegang krijgen tot de Status eigenschap van een taak om te bepalen of deze is gestart, is voltooid, is geannuleerd of een uitzondering heeft gegenereerd. De status wordt vertegenwoordigd door een TaskStatus opsomming.

Wanneer u een taak maakt, geeft u deze een gemachtigde van de gebruiker die de code inkapselt die door de taak wordt uitgevoerd. De gemachtigde kan worden uitgedrukt als een benoemde gemachtigde, een anonieme methode of een lambda-expressie. Lambda-expressies kunnen een aanroep naar een benoemde methode bevatten, zoals wordt weergegeven in het volgende voorbeeld. Het voorbeeld bevat een aanroep naar de Task.Wait methode om ervoor te zorgen dat de taak de uitvoering voltooit voordat de consolemodustoepassing wordt beëindigd.

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

public class Lambda
{
   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 as follows:
//       Hello from thread 'Main'.
//       Hello from taskA.
// or
//       Hello from taskA.
//       Hello from thread 'Main'.
Imports System.Threading

Namespace Lambda
    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.
End Namespace

U kunt ook de Task.Run methoden gebruiken om een taak in één bewerking te maken en te starten. Voor het beheren van de taak gebruiken de Run methoden de standaardtaakplanner, ongeacht welke taakplanner is gekoppeld aan de huidige thread. De Run methoden zijn de voorkeursmethode om taken te maken en te starten wanneer er geen controle meer nodig is over het maken en plannen van de taak.

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

namespace Run;

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 as follows:
//       Hello from thread 'Main'.
//       Hello from taskA.
// or
//       Hello from taskA.
//       Hello from thread 'Main'.
Imports System.Threading

Namespace Run
    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.
End Namespace

U kunt ook de TaskFactory.StartNew methode gebruiken om een taak in één bewerking te maken en te starten. Zoals wordt weergegeven in het volgende voorbeeld, kunt u deze methode gebruiken wanneer:

  • Het maken en plannen hoeft niet te worden gescheiden en u hebt aanvullende opties voor het maken van taken of het gebruik van een specifieke planner nodig.

  • U moet de extra status doorgeven aan de taak die u kunt ophalen via Task.AsyncState de eigenschap.

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

namespace TaskIntro;

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

public class AsyncState
{
    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, ran on thread #3.
//     Task #1 created at 635116412924607584, ran on thread #4.
//     Task #2 created at 635116412924607584, ran on thread #4.
//     Task #3 created at 635116412924607584, ran on thread #4.
//     Task #4 created at 635116412924607584, ran on thread #3.
//     Task #5 created at 635116412924607584, ran on thread #3.
//     Task #6 created at 635116412924607584, ran on thread #4.
//     Task #7 created at 635116412924607584, ran on thread #4.
//     Task #8 created at 635116412924607584, ran on thread #3.
//     Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading

Namespace AsyncState
    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 = Environment.CurrentManagedThreadId
                                                     End Sub,
                New CustomData With {.Name = i, .CreationTime = Date.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 635116412924597583, ran on thread #3.
    '     Task #1 created at 635116412924607584, ran on thread #4.
    '     Task #2 created at 635116412924607584, ran on thread #4.
    '     Task #3 created at 635116412924607584, ran on thread #4.
    '     Task #4 created at 635116412924607584, ran on thread #3.
    '     Task #5 created at 635116412924607584, ran on thread #3.
    '     Task #6 created at 635116412924607584, ran on thread #4.
    '     Task #7 created at 635116412924607584, ran on thread #4.
    '     Task #8 created at 635116412924607584, ran on thread #3.
    '     Task #9 created at 635116412924607584, ran on thread #4.
End Namespace

Task en Task<TResult> elk geeft een statische Factory eigenschap weer die een standaardexemplaren retourneert TaskFactory, zodat u de methode kunt aanroepen als Task.Factory.StartNew(). In het volgende voorbeeld, omdat de taken van het type System.Threading.Tasks.Task<TResult>zijn, hebben ze elk een openbare Task<TResult>.Result eigenschap die het resultaat van de berekening bevat. De taken worden asynchroon uitgevoerd en kunnen in elke volgorde worden voltooid. Als de eigenschap wordt geopend voordat de Result berekening is voltooid, blokkeert de eigenschap de aanroepende thread totdat de waarde beschikbaar is.

using System;
using System.Threading.Tasks;

public class Result
{
   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

Namespace Result
    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
End Namespace

Zie Procedure voor meer informatie : Een waarde retourneren van een taak.

Wanneer u een lambda-expressie gebruikt om een gemachtigde te maken, hebt u toegang tot alle variabelen die op dat punt in uw broncode zichtbaar zijn. In sommige gevallen, met name binnen lussen, legt een lambda de variabele echter niet vast zoals verwacht. Hiermee wordt alleen de verwijzing van de variabele vastgelegd, niet de waarde, omdat deze na elke iteratie muteert. In het volgende voorbeeld ziet u het probleem. Er wordt een lusteller doorgegeven aan een lambda-expressie waarmee een CustomData object wordt geïnstitueert en de lusteller wordt gebruikt als id van het object. Zoals de uitvoer uit het voorbeeld laat zien, heeft elk CustomData object een identieke id.

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

namespace Example.Iterations;

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

public class IterationTwo
{
   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

Namespace IterationsTwo
    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 = Date.Now.Ticks}
                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                         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.
End Namespace

U kunt de waarde voor elke iteratie openen door een statusobject aan een taak op te geven via de constructor. In het volgende voorbeeld wordt het vorige voorbeeld gewijzigd met behulp van de lusteller bij het maken van het CustomData object, dat op zijn beurt wordt doorgegeven aan de lambda-expressie. Zoals in de uitvoer van het voorbeeld wordt weergegeven, heeft elk CustomData object nu een unieke id op basis van de waarde van de lusteller op het moment dat het object is geïnstantieerd.

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

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

public class IterationOne
{
   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

Namespace IterationsOne
    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 = Environment.CurrentManagedThreadId
                                                         Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                         data.Name, data.CreationTime, data.ThreadNum)
                                                     End Sub,
                    New CustomData With {.Name = i, .CreationTime = Date.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.
End Namespace

Deze status wordt doorgegeven als argument aan de taakdelegatie en kan worden geopend vanuit het taakobject met behulp van de Task.AsyncState eigenschap. Het volgende voorbeeld is een variatie op het vorige voorbeeld. De eigenschap gebruikt de AsyncState eigenschap om informatie weer te geven over de CustomData objecten die worden doorgegeven aan de lambda-expressie.

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

namespace TaskIntro;

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

public class AsyncState
{
    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, ran on thread #3.
//     Task #1 created at 635116412924607584, ran on thread #4.
//     Task #2 created at 635116412924607584, ran on thread #4.
//     Task #3 created at 635116412924607584, ran on thread #4.
//     Task #4 created at 635116412924607584, ran on thread #3.
//     Task #5 created at 635116412924607584, ran on thread #3.
//     Task #6 created at 635116412924607584, ran on thread #4.
//     Task #7 created at 635116412924607584, ran on thread #4.
//     Task #8 created at 635116412924607584, ran on thread #3.
//     Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading

Namespace AsyncState
    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 = Environment.CurrentManagedThreadId
                                                     End Sub,
                New CustomData With {.Name = i, .CreationTime = Date.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 635116412924597583, ran on thread #3.
    '     Task #1 created at 635116412924607584, ran on thread #4.
    '     Task #2 created at 635116412924607584, ran on thread #4.
    '     Task #3 created at 635116412924607584, ran on thread #4.
    '     Task #4 created at 635116412924607584, ran on thread #3.
    '     Task #5 created at 635116412924607584, ran on thread #3.
    '     Task #6 created at 635116412924607584, ran on thread #4.
    '     Task #7 created at 635116412924607584, ran on thread #4.
    '     Task #8 created at 635116412924607584, ran on thread #3.
    '     Task #9 created at 635116412924607584, ran on thread #4.
End Namespace

Taak-id

Elke taak ontvangt een geheel getal-id die deze uniek identificeert in een toepassingsdomein en kan worden geopend met behulp van de Task.Id eigenschap. De id is handig voor het weergeven van taakgegevens in het visual Studio-foutopsporingsprogramma Parallelle stacks en takenvensters. De id wordt lazily gemaakt, wat betekent dat deze pas wordt gemaakt nadat deze is aangevraagd. Daarom kan een taak elke keer dat het programma wordt uitgevoerd een andere id hebben. Zie Het venster Taken gebruiken en het venster Parallelle stacks gebruiken voor meer informatie over het weergeven van taak-id's in het foutopsporingsprogramma.

Opties voor het maken van taken

De meeste API's die taken maken, bieden overbelastingen die een TaskCreationOptions parameter accepteren. Door een of meer van deze opties op te geven, laat u de taakplanner weten hoe de taak in de threadgroep moet worden gepland. Opties kunnen worden gecombineerd met behulp van een bitsgewijze OR-bewerking .

In het volgende voorbeeld ziet u een taak met de LongRunning en PreferFairness opties:

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

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

Taken, threads en cultuur

Elke thread heeft een gekoppelde cultuur en UI-cultuur, die respectievelijk worden gedefinieerd door de Thread.CurrentCulture en Thread.CurrentUICulture eigenschappen. De cultuur van een thread wordt gebruikt in bewerkingen zoals opmaak, parseren, sorteren en tekenreeksvergelijkingsbewerkingen. De UI-cultuur van een thread wordt gebruikt in het opzoeken van resources.

De systeemcultuur definieert de standaardcultuur en UI-cultuur van een thread. U kunt echter een standaardcultuur opgeven voor alle threads in een toepassingsdomein met behulp van de CultureInfo.DefaultThreadCurrentCulture en CultureInfo.DefaultThreadCurrentUICulture eigenschappen. Als u de cultuur van een thread expliciet instelt en een nieuwe thread start, neemt de nieuwe thread de cultuur van de aanroepende thread niet over; In plaats daarvan is de cultuur de standaardsysteemcultuur. In het programmeren op basis van taken wordt echter gebruikgemaakt van de cultuur van de aanroepende thread, zelfs als de taak asynchroon op een andere thread wordt uitgevoerd.

In het volgende voorbeeld ziet u een eenvoudige afbeelding. De huidige cultuur van de app wordt gewijzigd in Frans (Frankrijk). Als Frans (Frankrijk) al de huidige cultuur is, verandert het in het Engels (Verenigde Staten). Vervolgens wordt een gemachtigde aangeroepen die een aantal formatDelegate getallen retourneert die zijn opgemaakt als valutawaarden in de nieuwe cultuur. Of de gemachtigde nu wordt aangeroepen door een taak synchroon of asynchroon, de taak maakt gebruik van de cultuur van de aanroepende thread.

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

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 €
Imports System.Globalization
Imports System.Threading

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 €

Notitie

In versies van .NET Framework ouder dan .NET Framework 4.6 wordt de cultuur van een taak bepaald door de cultuur van de thread waarop deze wordt uitgevoerd, niet de cultuur van de aanroepende thread. Voor asynchrone taken kan de cultuur die door de taak wordt gebruikt, afwijken van de cultuur van de aanroepende thread.

Zie de sectie 'Bewerkingen op basis van cultuur en asynchrone taken' in het CultureInfo artikel voor meer informatie over asynchrone taken en cultuur.

Taakvervolgingen maken

Met de Task.ContinueWith en Task<TResult>.ContinueWith methoden kunt u een taak opgeven die moet worden gestart wanneer de antecedent-taak is voltooid. De gemachtigde van de vervolgtaak wordt een verwijzing doorgegeven naar de antecedent-taak, zodat deze de status van de antecedent-taak kan onderzoeken. En door de waarde van de Task<TResult>.Result eigenschap op te halen, kunt u de uitvoer van de antecedent gebruiken als invoer voor de voortzetting.

In het volgende voorbeeld wordt de getData taak gestart door een aanroep naar de TaskFactory.StartNew<TResult>(Func<TResult>) methode. De processData taak wordt automatisch gestart wanneer getData deze is voltooid en displayData wordt gestart wanneer processData deze is voltooid. getDataproduceert een matrix met gehele getallen, die toegankelijk is voor de processData taak via de eigenschap van Task<TResult>.Result de getData taak. De processData taak verwerkt die matrix en retourneert een resultaat waarvan het type wordt afgeleid van het retourtype van de lambda-expressie die aan de Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) methode is doorgegeven. De displayData taak wordt automatisch uitgevoerd wanneer processData deze is voltooid en het Tuple<T1,T2,T3> object dat door de processData lambda-expressie wordt geretourneerd, is toegankelijk voor de displayData taak via de eigenschap van Task<TResult>.Result de processData taak. De displayData taak krijgt het resultaat van de processData taak. Het produceert een resultaat waarvan het type op een vergelijkbare manier wordt afgeleid en die beschikbaar wordt gesteld aan het programma in de Result eigenschap.

using System;
using System.Threading.Tasks;

public class ContinuationOne
{
   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

Namespace ContinuationsOne
    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
End Namespace

Omdat Task.ContinueWith dit een instantiemethode is, kunt u methodeaanroepen koppelen in plaats van een Task<TResult> object te instantiëren voor elke antecedent-taak. Het volgende voorbeeld is functioneel identiek aan de vorige, behalve dat het aanroepen aan de Task.ContinueWith methode aan elkaar koppelt. Het Task<TResult> object dat wordt geretourneerd door de keten van methode-aanroepen is de uiteindelijke vervolgtaak.

using System;
using System.Threading.Tasks;

public class ContinuationTwo
{
   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

Namespace ContinuationsTwo
    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
End Namespace

Met de ContinueWhenAll en ContinueWhenAny methoden kunt u doorgaan vanuit meerdere taken.

Zie Koppelingstaken met behulp van vervolgtaken voor meer informatie.

Losgekoppelde onderliggende taken maken

Wanneer gebruikerscode die wordt uitgevoerd in een taak een nieuwe taak maakt en de optie niet opgeeft AttachedToParent , wordt de nieuwe taak niet gesynchroniseerd met de bovenliggende taak op een speciale manier. Dit type niet-gesynchroniseerde taak wordt een losgekoppelde geneste taak of een losgekoppelde onderliggende taak genoemd. In het volgende voorbeeld ziet u een taak waarmee één losgekoppelde onderliggende taak wordt gemaakt:

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.

Notitie

De bovenliggende taak wacht niet totdat de losgekoppelde onderliggende taak is voltooid.

Onderliggende taken maken

Wanneer gebruikerscode die wordt uitgevoerd in een taak een taak maakt met de AttachedToParent optie, wordt de nieuwe taak een gekoppelde onderliggende taak van de bovenliggende taak genoemd. U kunt de AttachedToParent optie gebruiken om parallelle uitvoering van gestructureerde taken uit te drukken, omdat de bovenliggende taak impliciet wacht tot alle gekoppelde onderliggende taken zijn voltooid. In het volgende voorbeeld ziet u een bovenliggende taak waarmee 10 gekoppelde onderliggende taken worden gemaakt. In het voorbeeld wordt de Task.Wait methode aangeroepen om te wachten totdat de bovenliggende taak is voltooid. Het hoeft niet expliciet te wachten totdat de gekoppelde onderliggende taken zijn voltooid.

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

public class Child
{
   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

Namespace Child
    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.
End Namespace

Een bovenliggende taak kan de TaskCreationOptions.DenyChildAttach optie gebruiken om te voorkomen dat andere taken aan de bovenliggende taak worden gekoppeld. Zie Gekoppelde en losgekoppelde onderliggende taken voor meer informatie.

Wachten totdat taken zijn voltooid

De System.Threading.Tasks.Task typen bieden System.Threading.Tasks.Task<TResult> verschillende overbelastingen van de Task.Wait methoden waarmee u kunt wachten tot een taak is voltooid. Bovendien kunt u met overbelasting van de statische Task.WaitAll en Task.WaitAny methoden wachten tot een of alle taken zijn voltooid.

Normaal gesproken wacht u op een taak om een van deze redenen:

  • De hoofdthread is afhankelijk van het uiteindelijke resultaat dat door een taak wordt berekend.

  • U moet uitzonderingen afhandelen die kunnen worden gegenereerd vanuit de taak.

  • De toepassing kan worden beëindigd voordat alle taken zijn uitgevoerd. Consoletoepassingen worden bijvoorbeeld beëindigd nadat alle synchrone code in Main (het ingangspunt van de toepassing) is uitgevoerd.

In het volgende voorbeeld ziet u het basispatroon dat geen uitzonderingsafhandeling omvat:

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...

Zie Uitzonderingsafhandeling voor een voorbeeld van uitzonderingsafhandeling.

Bij sommige overbelastingen kunt u een time-out opgeven en andere nemen een extra CancellationToken invoerparameter als invoerparameter, zodat het wachten zelf programmatisch of als reactie op gebruikersinvoer kan worden geannuleerd.

Wanneer u wacht op een taak, wacht u impliciet op alle onderliggende items van die taak die zijn gemaakt met behulp van de TaskCreationOptions.AttachedToParent optie. Task.Wait retourneert onmiddellijk als de taak al is voltooid. Een Task.Wait methode genereert eventuele uitzonderingen die door een taak worden gegenereerd, zelfs als de Task.Wait methode is aangeroepen nadat de taak is voltooid.

Taken opstellen

De Task en Task<TResult> klassen bieden verschillende methoden om u te helpen bij het opstellen van meerdere taken. Deze methoden implementeren algemene patronen en maken beter gebruik van de asynchrone taalfuncties die worden geleverd door C#, Visual Basic en F#. In deze sectie worden de WhenAllmethoden , WhenAny, Delayen FromResult methoden beschreven.

Task.WhenAll

De Task.WhenAll methode wacht asynchroon totdat meerdere Task objecten zijn Task<TResult> voltooid. Het biedt overbelaste versies waarmee u kunt wachten op niet-uniforme sets taken. U kunt bijvoorbeeld wachten tot meerdere Task objecten zijn Task<TResult> voltooid vanaf één methode-aanroep.

Task.WhenAny

De Task.WhenAny methode wacht asynchroon tot een van de meerdere Task objecten is Task<TResult> voltooid. Net als in de Task.WhenAll methode biedt deze methode overbelaste versies waarmee u kunt wachten op niet-uniforme sets taken. De WhenAny methode is vooral handig in de volgende scenario's:

  • Redundante bewerkingen: Overweeg een algoritme of bewerking die op veel manieren kan worden uitgevoerd. U kunt de WhenAny methode gebruiken om de bewerking te selecteren die eerst is voltooid en vervolgens de resterende bewerkingen te annuleren.

  • Interleaved-bewerkingen: u kunt meerdere bewerkingen starten die moeten worden voltooid en de WhenAny methode gebruiken om resultaten te verwerken wanneer elke bewerking is voltooid. Nadat één bewerking is voltooid, kunt u een of meer taken starten.

  • Beperkte bewerkingen: u kunt de WhenAny methode gebruiken om het vorige scenario uit te breiden door het aantal gelijktijdige bewerkingen te beperken.

  • Verlopen bewerkingen: u kunt de WhenAny methode gebruiken om te selecteren tussen een of meer taken en een taak die na een bepaalde tijd eindigt, zoals een taak die door de Delay methode wordt geretourneerd. De Delay methode wordt beschreven in de volgende sectie.

Task.Delay

De Task.Delay methode produceert een Task object dat na de opgegeven tijd is voltooid. U kunt deze methode gebruiken om lussen te bouwen die gegevens opvragen, om time-outs op te geven, om de verwerking van gebruikersinvoer te vertragen, enzovoort.

Taak(T). FromResult

Met behulp van de Task.FromResult methode kunt u een object maken dat een Task<TResult> vooraf berekend resultaat bevat. Deze methode is handig wanneer u een asynchrone bewerking uitvoert die een Task<TResult> object retourneert en het resultaat van dat Task<TResult> object al wordt berekend. Voor een voorbeeld dat wordt gebruikt FromResult om de resultaten van asynchrone downloadbewerkingen op te halen die in een cache worden bewaard, raadpleegt u Procedure: Vooraf berekende taken maken.

Uitzonderingen verwerken in taken

Wanneer een taak een of meer uitzonderingen genereert, worden de uitzonderingen verpakt in een AggregateException uitzondering. Deze uitzondering wordt weer doorgegeven aan de thread die wordt samengevoegd met de taak. Normaal gesproken is het de thread die wacht tot de taak is voltooid of de thread die toegang heeft tot de Result eigenschap. Dit gedrag dwingt het .NET Framework-beleid af dat alle niet-verwerkte uitzonderingen standaard het proces moeten beëindigen. De aanroepende code kan de uitzonderingen verwerken met behulp van een van de volgende items in een try/catch blok:

De samenvoegingsthread kan ook uitzonderingen verwerken door toegang te krijgen tot de Exception eigenschap voordat de taak garbagecollection wordt verzameld. Als u deze eigenschap opent, voorkomt u dat de niet-verwerkte uitzondering het doorgiftegedrag van de uitzondering activeert waarmee het proces wordt beëindigd wanneer het object is voltooid.

Zie Uitzonderingsafhandeling voor meer informatie over uitzonderingen en taken.

Taken annuleren

De Task klasse ondersteunt coöperatieve annulering en is volledig geïntegreerd met de System.Threading.CancellationTokenSource en System.Threading.CancellationToken klassen, die zijn geïntroduceerd in .NET Framework 4. Veel van de constructors in de System.Threading.Tasks.Task klasse nemen een CancellationToken object als invoerparameter. Veel van de StartNew en Run overbelastingen bevatten ook een CancellationToken parameter.

U kunt het token maken en de annuleringsaanvraag op een later tijdstip uitgeven met behulp van de CancellationTokenSource klasse. Geef het token door aan het Task argument en verwijs ook naar hetzelfde token in uw gebruikersdelegatie, wat het werk doet om te reageren op een annuleringsaanvraag.

Zie Taakannulering en Procedures voor meer informatie: Een taak en de onderliggende items annuleren.

De klasse TaskFactory

De TaskFactory klasse biedt statische methoden waarmee algemene patronen worden ingekapseld voor het maken en starten van taken en vervolgtaken.

De standaardwaarde TaskFactory kan worden geopend als een statische eigenschap in de Task klasse of Task<TResult> klasse. U kunt ook rechtstreeks een TaskFactory instantie maken en verschillende opties opgeven die een CancellationToken, een TaskCreationOptions optie, een TaskContinuationOptions optie of een TaskScheduler. De opties die worden opgegeven wanneer u de taakfactory maakt, worden toegepast op alle taken die worden gemaakt, tenzij deze Task worden gemaakt met behulp van de TaskCreationOptions opsomming. In dat geval overschrijven de opties van de taak deze van de taakfactory.

Taken zonder gemachtigden

In sommige gevallen kunt u een asynchrone Task bewerking inkapselen die wordt uitgevoerd door een extern onderdeel in plaats van de gemachtigde van de gebruiker. Als de bewerking is gebaseerd op het begin-/eindpatroon van het Asynchrone programmeermodel, kunt u de FromAsync methoden gebruiken. Als dat niet het geval is, kunt u het TaskCompletionSource<TResult> object gebruiken om de bewerking in een taak te verpakken en zo een aantal voordelen van Task programmeerbaarheid te krijgen. Bijvoorbeeld ondersteuning voor het doorgeven van uitzonderingen en vervolgen. Zie TaskCompletionSource<TResult> voor meer informatie.

Aangepaste planners

De meeste ontwikkelaars van toepassingen of bibliotheken maken niet uit op welke processor de taak wordt uitgevoerd, hoe het werk synchroniseert met andere taken of hoe deze is gepland op de System.Threading.ThreadPooltaak. Ze vereisen alleen dat het zo efficiënt mogelijk wordt uitgevoerd op de hostcomputer. Als u meer gedetailleerde controle over de planningsgegevens nodig hebt, kunt u met de TPL enkele instellingen configureren voor de standaardtaakplanner en kunt u zelfs een aangepaste planner opgeven. Zie TaskScheduler voor meer informatie.

De TPL heeft verschillende nieuwe openbare typen die nuttig zijn in parallelle en sequentiële scenario's. Deze omvatten verschillende thread-veilige, snelle en schaalbare verzamelingsklassen in de System.Collections.Concurrent naamruimte en verschillende nieuwe synchronisatietypen. EnSystem.Threading.ManualResetEventSlim, die bijvoorbeeld System.Threading.Semaphore efficiënter zijn dan hun voorgangers voor specifieke soorten workloads. Andere nieuwe typen in .NET Framework 4 bieden bijvoorbeeld System.Threading.BarrierSystem.Threading.SpinLockfunctionaliteit die niet beschikbaar was in eerdere versies. Zie Gegevensstructuren voor parallel programmeren voor meer informatie.

Aangepaste taaktypen

U wordt aangeraden niet over te nemen van System.Threading.Tasks.Task of System.Threading.Tasks.Task<TResult>. In plaats daarvan raden we u aan de AsyncState eigenschap te gebruiken om extra gegevens of statussen te koppelen aan een Task of Task<TResult> object. U kunt ook uitbreidingsmethoden gebruiken om de functionaliteit van de Task en Task<TResult> klassen uit te breiden. Zie Extensiemethoden en Extensiemethoden voor meer informatie over extensiemethoden.

Als u moet overnemen van Task ofTask<TResult>, kunt u de , System.Threading.Tasks.TaskFactory<TResult>of klassen System.Threading.Tasks.TaskFactoryniet gebruiken Run om System.Threading.Tasks.TaskCompletionSource<TResult> exemplaren van uw aangepaste taaktype te maken. U kunt ze niet gebruiken omdat deze klassen alleen Task worden gemaakt en Task<TResult> objecten. Bovendien kunt u niet de mechanismen voor taakvervolging gebruiken die worden geleverd doorTask, Task<TResult>en TaskFactoryTaskFactory<TResult> om exemplaren van uw aangepaste taaktype te maken. U kunt ze niet gebruiken omdat deze klassen ook alleen Task en Task<TResult> objecten maken.

Title Beschrijving
Taken koppelen met behulp van vervolgtaken Hierin wordt beschreven hoe vervolgen werken.
Gekoppelde en losgekoppelde onderliggende taken Beschrijft het verschil tussen gekoppelde en losgekoppelde onderliggende taken.
Taakannulering Beschrijft de annuleringsondersteuning die is ingebouwd in het Task object.
Afhandeling van uitzonderingen Hierin wordt beschreven hoe uitzonderingen op gelijktijdige threads worden verwerkt.
Procedure: Parallel.Invoke gebruiken om parallelle bewerkingen uit te voeren Hierin wordt beschreven hoe u deze kunt gebruiken Invoke.
Procedure: een waarde van een taak retourneren Hierin wordt beschreven hoe u waarden retourneert uit taken.
Procedure: Een taak en de onderliggende items annuleren Hierin wordt beschreven hoe u taken annuleert.
Procedure: Vooraf berekende taken maken Beschrijft hoe u de Task.FromResult methode gebruikt om de resultaten van asynchrone downloadbewerkingen op te halen die in een cache worden bewaard.
Procedure: een binaire structuur doorlopen met parallelle taken Hierin wordt beschreven hoe u taken gebruikt om een binaire structuur te doorlopen.
Procedure: Een geneste taak uitpakken Demonstreert hoe u de Unwrap extensiemethode gebruikt.
Gegevensparallellisme Hierin wordt beschreven hoe u parallelle lussen voor gegevens gebruikt For en ForEach maakt.
Parallel programmeren Knooppunt op het hoogste niveau voor parallelle programmering van .NET Framework.

Zie ook