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 DoSomeWork
en 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. getData
produceert 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.
Het meest voorkomende patroon is StartNew, waarmee een taak in één instructie wordt gemaakt en gestart.
Wanneer u vervolgtaken maakt op basis van meerdere antecedenten, gebruikt u de ContinueWhenAll methode of ContinueWhenAny methode of hun equivalenten in de Task<TResult> klasse. Zie Koppelingstaken met behulp van vervolgtaken voor meer informatie.
Als u asynchrone programmeermodellen
BeginX
enEndX
methoden in een Task of Task<TResult> meer exemplaren wilt inkapselen, gebruikt u de FromAsync methoden. Zie TPL en Traditioneel .NET Framework Asynchroon programmeren voor meer informatie.
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.
Gerelateerde gegevensstructuren
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.
Verwante secties
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. |