Programmation asynchrone basée sur des tâches
La bibliothèque parallèle de tâches (TPL) est basée sur le concept d’une tâche , qui représente une opération asynchrone. Sous certains aspects, une tâche ressemble à un thread ou un élément de travail ThreadPool, mais à un niveau supérieur d’abstraction. Le terme parallélisme des tâches fait référence à une ou plusieurs tâches indépendantes exécutées simultanément. Les tâches offrent deux principaux avantages :
Utilisation plus efficace et plus évolutive des ressources système.
En arrière-plan, les tâches sont mises en file d’attente vers ThreadPool, qui a été améliorée avec des algorithmes qui déterminent et s’ajustent au nombre de threads. Ces algorithmes fournissent un équilibrage de charge pour optimiser le débit. Ce processus rend les tâches relativement légères et vous pouvez créer un grand nombre d’entre elles pour permettre un parallélisme affiné.
Plus de contrôle programmatique qu'il n'est possible avec un thread ou une tâche.
Les tâches et l’infrastructure créées autour d’elles fournissent un ensemble complet d’API qui prennent en charge l’attente, l’annulation, les continuations, la gestion robuste des exceptions, l’état détaillé, la planification personnalisée, etc.
Pour les deux raisons, TPL est l’API préférée pour écrire du code multithread, asynchrone et parallèle dans .NET.
Création et exécution de tâches implicitement
La méthode Parallel.Invoke offre un moyen pratique d’exécuter simultanément n’importe quel nombre d’instructions arbitraires. Pour cela, passez un délégué Action pour chaque élément de travail. Le moyen le plus simple de créer ces délégués consiste à utiliser des expressions lambda. L’expression lambda peut appeler une méthode nommée ou fournir le code inline. L’exemple suivant montre un appel de base Invoke qui crée et démarre deux tâches qui s’exécutent simultanément. La première tâche est représentée par une expression lambda qui appelle une méthode nommée DoSomeWork
, et la deuxième tâche est représentée par une expression lambda qui appelle une méthode nommée DoSomeOtherWork
.
Remarque
Cette documentation utilise des expressions lambda pour définir des délégués dans TPL. Si vous n’êtes pas familiarisé avec les expressions lambda en C# ou Visual Basic, consultez expressions lambda dans PLINQ et TPL.
Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())
Remarque
Le nombre d’instances Task créées en arrière-plan par Invoke n’est pas nécessairement égal au nombre de délégués fournis. Le TPL peut utiliser différentes optimisations, en particulier avec un grand nombre de délégués.
Pour plus d’informations, consultez Guide pratique pour utiliser Parallel.Invoke pour exécuter des opérations parallèles.
Pour un meilleur contrôle sur l’exécution des tâches ou pour retourner une valeur de la tâche, vous devez travailler plus explicitement avec les objets Task.
Création et exécution de tâches explicitement
Une tâche qui ne retourne pas de valeur est représentée par la classe System.Threading.Tasks.Task. Une tâche qui retourne une valeur est représentée par la classe System.Threading.Tasks.Task<TResult>, qui hérite de Task. L’objet de tâche gère les détails de l’infrastructure et fournit des méthodes et des propriétés accessibles à partir du thread appelant tout au long de la durée de vie de la tâche. Par exemple, vous pouvez accéder à la propriété Status d’une tâche à tout moment pour déterminer si elle a commencé à s’exécuter, a été exécutée jusqu’à l’achèvement, a été annulée ou a levée une exception. L’état est représenté par une énumération TaskStatus.
Lorsque vous créez une tâche, vous lui donnez un délégué d’utilisateur qui encapsule le code que la tâche exécutera. Le délégué peut être exprimé sous la forme d’un délégué nommé, d’une méthode anonyme ou d’une expression lambda. Les expressions lambda peuvent contenir un appel à une méthode nommée, comme illustré dans l’exemple suivant. L’exemple inclut un appel à la méthode Task.Wait pour s’assurer que la tâche termine l’exécution avant la fin de l’application en mode console.
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
Vous pouvez également utiliser les méthodes Task.Run pour créer et démarrer une tâche en une seule opération. Pour gérer la tâche, les méthodes Run utilisent le planificateur de tâches par défaut, quel que soit le planificateur de tâches associé au thread actuel. Les méthodes Run sont la meilleure façon de créer et de démarrer des tâches lorsque plus de contrôle sur la création et la planification de la tâche n’est pas nécessaire.
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
Vous pouvez également utiliser la méthode TaskFactory.StartNew pour créer et démarrer une tâche en une seule opération. Comme illustré dans l’exemple suivant, vous pouvez utiliser cette méthode quand :
La création et la planification n’ont pas besoin d’être séparées et vous avez besoin d’options de création de tâches supplémentaires ou de l’utilisation d’un planificateur spécifique.
Vous devez passer un état supplémentaire dans la tâche que vous pouvez récupérer via sa propriété Task.AsyncState.
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 et Task<TResult> chacun expose une propriété de Factory statique qui retourne une instance par défaut de TaskFactory, afin que vous puissiez appeler la méthode comme Task.Factory.StartNew()
. En outre, dans l’exemple suivant, étant donné que les tâches sont de type System.Threading.Tasks.Task<TResult>, elles ont chacune une propriété de Task<TResult>.Result publique qui contient le résultat du calcul. Les tâches s’exécutent de manière asynchrone et peuvent se terminer dans n’importe quel ordre. Si la propriété Result est accessible avant la fin du calcul, la propriété bloque le thread appelant jusqu’à ce que la valeur soit disponible.
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
Pour plus d’informations, consultez Comment : retourner une valeur à partir d'une tâche.
Lorsque vous utilisez une expression lambda pour créer un délégué, vous avez accès à toutes les variables visibles à ce stade dans votre code source. Toutefois, dans certains cas, notamment dans les boucles, une lambda ne capture pas la variable comme prévu. Elle capture uniquement la référence de la variable, et non la valeur, car elle mute après chaque itération. L’exemple suivant illustre le problème. Il transmet un compteur de boucle à une expression lambda qui instancie un objet CustomData
et utilise le compteur de boucle comme identificateur de l’objet. Comme le montre la sortie de l’exemple, chaque objet CustomData
a un identificateur identique.
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
Vous pouvez accéder à la valeur sur chaque itération en fournissant un objet d’état à une tâche via son constructeur. L’exemple suivant modifie l’exemple précédent à l’aide du compteur de boucles lors de la création de l’objet CustomData
, qui, à son tour, est passé à l’expression lambda. Comme le montre la sortie de l’exemple, chaque objet CustomData
a maintenant un identificateur unique basé sur la valeur du compteur de boucle au moment où l’objet a été instancié.
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
Cet état est passé en tant qu’argument au délégué de tâche, et il est accessible à partir de l’objet de tâche à l’aide de la propriété Task.AsyncState. L’exemple suivant est une variante de l’exemple précédent. Il utilise la propriété AsyncState pour afficher des informations sur les objets CustomData
passés à l’expression lambda.
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
ID de tâche
Chaque tâche reçoit un ID entier qui l’identifie de manière unique dans un domaine d’application et est accessible à l’aide de la propriété Task.Id. L’ID est utile pour la consultation des informations de tâche dans les fenêtres Piles parallèles et Tâches du débogueur Visual Studio. L’identifiant est créé tardivement, ce qui signifie qu’il n’est pas créé tant qu’il n’est pas demandé. Par conséquent, une tâche peut avoir un ID différent chaque fois que le programme est exécuté. Pour plus d’informations sur l’affichage des ID de tâche dans le débogueur, consultez Utilisation de la fenêtre Tâches et Utilisation de la fenêtre Piles parallèles.
Options de création de tâche
La plupart des API qui créent des tâches fournissent des surcharges qui acceptent un paramètre TaskCreationOptions. En spécifiant une ou plusieurs de ces options, vous indiquez au planificateur de tâches comment planifier la tâche sur le pool de threads. Les options peuvent être combinées avec une opération de bits OR.
L’exemple suivant montre une tâche qui a les options LongRunning et PreferFairness :
var task3 = new Task(() => MyLongRunningMethod(),
TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();
Dim task3 = New Task(Sub() MyLongRunningMethod(),
TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()
Tâches, threads et culture
Chaque thread a une culture et une culture d’interface utilisateur associées, qui sont définies par les propriétés Thread.CurrentCulture et Thread.CurrentUICulture, respectivement. La culture d’un thread est utilisée dans les opérations telles que la mise en forme, l’analyse, le tri et les opérations de comparaison de chaînes. La culture de l’interface utilisateur d’un thread est utilisée dans la recherche de ressources.
La culture système définit la culture par défaut et la culture de l’interface utilisateur d’un thread. Toutefois, vous pouvez spécifier une culture par défaut pour tous les threads d’un domaine d’application à l’aide des propriétés CultureInfo.DefaultThreadCurrentCulture et CultureInfo.DefaultThreadCurrentUICulture. Si vous définissez explicitement la culture d’un thread et lancez un nouveau thread, le nouveau thread n’hérite pas de la culture du thread appelant ; Au lieu de cela, sa culture est la culture système par défaut. Toutefois, dans la programmation basée sur des tâches, les tâches utilisent la culture du thread appelant, même si la tâche s’exécute de manière asynchrone sur un autre thread.
L’exemple suivant fournit une illustration simple. Il modifie la culture actuelle de l’application en français (France). Si le français (France) est déjà la culture actuelle, il passe à l’anglais (États-Unis). Il appelle ensuite un délégué nommé formatDelegate
qui retourne des nombres mis en forme comme valeurs monétaires dans la nouvelle culture. Que le délégué soit appelé par une tâche de manière synchrone ou asynchrone, la tâche utilise la culture du thread appelant.
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 €
Remarque
Dans les versions de .NET Framework antérieures à .NET Framework 4.6, la culture d’une tâche est déterminée par la culture du thread sur lequel elle s’exécute, et non par la culture du thread appelant. Pour les tâches asynchrones, la culture utilisée par la tâche peut être différente de la culture du thread appelant.
Pour plus d’informations sur les tâches et la culture asynchrones, consultez la section « Opérations basées sur la culture et les tâches asynchrones » dans l’article CultureInfo.
Création de continuations de tâches
Les méthodes Task.ContinueWith et Task<TResult>.ContinueWith vous permettent de spécifier une tâche à démarrer lorsque la tâche antécédente se termine. Le délégué de la tâche de continuation se voit attribuer une référence à la tâche antécédente afin d'examiner le statut de la tâche antécédente. Et en récupérant la valeur de la propriété Task<TResult>.Result, vous pouvez utiliser la sortie de l’antécédent comme entrée pour la continuation.
Dans l’exemple suivant, la tâche getData
est démarrée par un appel à la méthode TaskFactory.StartNew<TResult>(Func<TResult>). La tâche processData
est démarrée automatiquement lorsque getData
se termine, et displayData
est démarré lorsque processData
se termine. getData
produit un tableau entier, accessible à la tâche processData
par le biais de la propriété Task<TResult>.Result de la tâche getData
. La tâche processData
traite ce tableau et retourne un résultat dont le type est déduit du type de retour de l’expression lambda passée à la méthode Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>). La tâche displayData
s’exécute automatiquement lorsque processData
se termine, et l’objet Tuple<T1,T2,T3> retourné par l’expression lambda processData
est accessible à la tâche displayData
par le biais de la propriété Task<TResult>.Result de la tâche processData
. La tâche displayData
prend le résultat de la tâche processData
. Il produit un résultat dont le type est déduit de la même manière et qui est mis à la disposition du programme dans la propriété Result.
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
Étant donné que Task.ContinueWith est une méthode d’instance, vous pouvez chaîner des appels de méthode au lieu d’instancier un objet Task<TResult> pour chaque tâche antérieure. L’exemple suivant est fonctionnellement identique à celui précédent, sauf qu’il associe les appels à la méthode Task.ContinueWith. L’objet Task<TResult> retourné par la chaîne d’appels de méthode est la tâche de continuation finale.
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
Les méthodes ContinueWhenAll et ContinueWhenAny vous permettent de continuer à partir de plusieurs tâches.
Pour plus d’informations, consultez Chaînage des tâches à l’aide de tâches de continuation.
Création de tâches enfants détachées
Lorsque le code utilisateur en cours d’exécution dans une tâche crée une tâche et ne spécifie pas l’option AttachedToParent, la nouvelle tâche n’est pas synchronisée avec la tâche parente d’une manière particulière. Ce type de tâche non synchronisée est appelé tâche imbriquée détachée ou tâche enfant détachée. L’exemple suivant montre une tâche qui crée une tâche enfant détachée :
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.
Remarque
La tâche parent n’attend pas que la tâche enfant détachée soit terminée.
Création de tâches enfants
Lorsque le code utilisateur qui s’exécute dans une tâche crée une tâche avec l’option AttachedToParent, la nouvelle tâche est une tâche enfant attachée de la tâche d’origine, appelée tâche parent. Vous pouvez utiliser l’option AttachedToParent pour exprimer le parallélisme des tâches structuré, car la tâche parent attend implicitement que toutes les tâches enfants attachées soient terminées. L’exemple suivant montre une tâche parente qui crée 10 tâches enfants jointes. L’exemple appelle la méthode Task.Wait pour attendre que la tâche parente se termine. Il n’est pas nécessaire d’attendre explicitement que les tâches enfants attachées soient terminées.
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
Une tâche parente peut utiliser l’option TaskCreationOptions.DenyChildAttach pour empêcher d’autres tâches de s’attacher à la tâche parente. Pour plus d'informations, consultez Tâches enfants attachées et détachées.
Attente de fin d’exécution de tâches
Les types System.Threading.Tasks.Task et System.Threading.Tasks.Task<TResult> fournissent plusieurs surcharges des méthodes Task.Wait qui vous permettent d’attendre qu’une tâche soit terminée. En outre, les surcharges des méthodes statiques Task.WaitAll et Task.WaitAny vous permettent d'attendre que n'importe lequel ou tous les éléments d'un tableau de tâches soient terminés.
En règle générale, vous attendez une tâche pour l’une des raisons suivantes :
Le thread principal dépend du résultat final calculé par une tâche.
Vous devez gérer les exceptions pouvant être levées depuis la tâche.
L’application peut se terminer avant que toutes les tâches aient terminé l’exécution. Par exemple, les applications console se terminent après l’exécution de tout le code synchrone dans
Main
(point d’entrée de l’application).
L’exemple suivant montre le modèle de base qui n’implique pas la gestion des exceptions :
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...
Pour obtenir un exemple montrant la gestion des exceptions, consultez gestion des exceptions.
Certaines surcharges vous permettent de spécifier un délai d’attente, et d’autres prennent un CancellationToken supplémentaire en tant que paramètre d’entrée, afin que l’attente puisse être annulée soit par programmation, soit en réponse à l’entrée utilisateur.
Lorsque vous attendez une tâche, vous attendez implicitement tous les enfants de cette tâche créés à l'aide de l'option TaskCreationOptions.AttachedToParent. Task.Wait retourne immédiatement si la tâche est déjà terminée. Une méthode Task.Wait lève toutes les exceptions déclenchées par une tâche, même si la méthode Task.Wait a été appelée une fois la tâche terminée.
Composition des tâches
Les classes Task et Task<TResult> fournissent plusieurs méthodes pour vous aider à composer plusieurs tâches. Ces méthodes implémentent des modèles courants et utilisent mieux les fonctionnalités de langage asynchrone fournies par C#, Visual Basic et F#. Cette section décrit les méthodes WhenAll, WhenAny, Delayet FromResult.
Task.WhenAll
La méthode Task.WhenAll attend de façon asynchrone la fin de plusieurs objets Task ou Task<TResult>. Elle fournit des versions surchargées qui vous permettent d’attendre des ensembles de tâches non-uniformes. Par exemple, vous pouvez attendre que plusieurs objets Task et Task<TResult> soient terminés à partir d'un appel de méthode.
Task.WhenAny
La méthode Task.WhenAny attend de façon asynchrone la fin de l’un de plusieurs objets Task ou Task<TResult>. Comme dans la méthode Task.WhenAll, cette méthode fournit des versions surchargées qui vous permettent d’attendre des ensembles de tâches non uniformes. La méthode WhenAny est particulièrement utile dans les scénarios suivants :
opérations redondantes: considérez un algorithme ou une opération qui peut être effectué de plusieurs façons. Vous pouvez utiliser la méthode WhenAny pour sélectionner l’opération qui se termine en premier, puis annuler les opérations restantes.
opérations entrelacées: vous pouvez démarrer plusieurs opérations qui doivent se terminer et utiliser la méthode WhenAny pour traiter les résultats à mesure que chaque opération se termine. Une fois l’opération terminée, vous pouvez démarrer une ou plusieurs tâches.
opérations limitées: vous pouvez utiliser la méthode WhenAny pour étendre le scénario précédent en limitant le nombre d’opérations simultanées.
Opérations arrivées à expiration: vous pouvez utiliser la méthode WhenAny pour choisir parmi une ou plusieurs tâches, ainsi qu’une tâche qui se termine après un moment spécifique, telle qu’une tâche retournée par la méthode Delay. La méthode Delay est décrite dans la section suivante.
Task.Delay
La méthode Task.Delay produit un objet Task qui se termine après l’heure spécifiée. Vous pouvez utiliser cette méthode pour générer des boucles qui interrogent les données, pour spécifier des délais d’attente, pour retarder la gestion des entrées utilisateur, et ainsi de suite.
Task(T).FromResult
À l’aide de la méthode Task.FromResult, vous pouvez créer un objet Task<TResult> qui contient un résultat pré-calculé. Cette méthode est utile lorsque vous effectuez une opération asynchrone qui retourne un objet Task<TResult> et que le résultat de cet objet Task<TResult> est déjà calculé. Pour obtenir un exemple qui utilise FromResult pour récupérer les résultats des opérations de téléchargement asynchrones conservées dans un cache, consultez Guide pratique pour créer des tâches pré-calculées.
Gestion des exceptions dans les tâches
Lorsqu’une tâche lève une ou plusieurs exceptions, les exceptions sont encapsulées dans une exception AggregateException. Cette exception est propagée vers le thread qui se joint à la tâche. En règle générale, il s’agit du thread qui attend que la tâche se termine ou que le thread accède à la propriété Result. Ce comportement applique la stratégie .NET Framework que toutes les exceptions non gérées par défaut doivent mettre fin au processus. Le code appelant peut gérer les exceptions à l’aide de l’un des éléments suivants dans un bloc try
/catch
:
Le thread joint peut également gérer des exceptions en accédant à la propriété Exception avant que la tâche ne soit récupérée par le garbage collector. En accédant à cette propriété, vous empêchez l’exception non gérée de déclencher le comportement de propagation d’exception qui met fin au processus lorsque l’objet est finalisé.
Pour plus d’informations sur les exceptions et les tâches, consultez gestion des exceptions.
Annulation des tâches
La classe Task prend en charge l’annulation coopérative et est entièrement intégrée aux classes System.Threading.CancellationTokenSource et System.Threading.CancellationToken, qui ont été introduites dans le .NET Framework 4. La plupart des constructeurs de la classe System.Threading.Tasks.Task prennent un objet CancellationToken comme paramètre d’entrée. La plupart des surcharges StartNew et Run incluent également un paramètre CancellationToken.
Vous pouvez créer le jeton et émettre la demande d’annulation ultérieurement à l’aide de la classe CancellationTokenSource. Passez le jeton au Task en tant qu'argument et référencez ce même jeton dans votre délégué d'utilisateur, qui répond à une requête d'annulation.
Pour plus d’informations, consultez Annulation de tâches et Comment : annuler une tâche et ses enfants.
Classe TaskFactory
La classe TaskFactory fournit des méthodes statiques qui encapsulent des modèles courants pour créer et démarrer des tâches et des tâches de continuation.
Le modèle le plus courant est StartNew, qui crée et démarre une tâche dans une instruction.
Lorsque vous créez des tâches de continuation à partir de plusieurs antécédents, utilisez la méthode ContinueWhenAll ou ContinueWhenAny méthode ou leurs équivalents dans la classe Task<TResult>. Pour plus d’informations, consultez Chaînage des tâches à l’aide de tâches de continuation.
Pour encapsuler des méthodes
BeginX
de modèle de programmation asynchrone etEndX
dans une instance Task ou Task<TResult>, utilisez les méthodes FromAsync. Pour plus d’informations, consultez TPL et la programmation asynchrone .NET Framework traditionnelle.
La TaskFactory par défaut est accessible en tant que propriété statique sur la classe Task ou Task<TResult> classe. Vous pouvez également instancier un TaskFactory directement et spécifier différentes options qui incluent un CancellationToken, une option de TaskCreationOptions, une option de TaskContinuationOptions ou un TaskScheduler. Toutes les options spécifiées lorsque vous créez la fabrique de tâches sont appliquées à toutes les tâches qu’elle crée, sauf si le Task est créé à l’aide de l’énumération TaskCreationOptions, auquel cas les options de la tâche remplacent celles de la fabrique de tâches.
Tâches sans délégués
Dans certains cas, vous pouvez utiliser un Task pour encapsuler une opération asynchrone effectuée par un composant externe au lieu de votre délégué utilisateur. Si l’opération est basée sur le modèle de début/fin du modèle de programmation asynchrone, vous pouvez utiliser les méthodes FromAsync. Si ce n’est pas le cas, vous pouvez utiliser l’objet TaskCompletionSource<TResult> pour encapsuler l’opération dans une tâche et ainsi bénéficier de certains des avantages de Task programmabilité. Par exemple, la prise en charge de la propagation des exceptions et des continuations. Pour plus d’informations, consultez TaskCompletionSource<TResult>.
Planificateurs personnalisés
La plupart des développeurs d’applications ou de bibliothèques ne s’occupent pas du processeur sur lequel la tâche s’exécute, de la façon dont elle synchronise son travail avec d’autres tâches ou de la façon dont elle est planifiée sur le System.Threading.ThreadPool. Ils nécessitent uniquement qu’il s’exécute aussi efficacement que possible sur l’ordinateur hôte. Si vous avez besoin d’un contrôle plus précis sur les détails de planification, le TPL vous permet de configurer certains paramètres sur le planificateur de tâches par défaut, et même de fournir un planificateur personnalisé. Pour plus d’informations, consultez TaskScheduler.
Structures de données associées
Le TPL a plusieurs nouveaux types publics qui sont utiles dans des scénarios parallèles et séquentiels. Il s’agit notamment de plusieurs classes de collection thread-safe, rapides et évolutives dans l’espace de noms System.Collections.Concurrent et plusieurs nouveaux types de synchronisation. Par exemple, System.Threading.Semaphore et System.Threading.ManualResetEventSlim, qui sont plus efficaces que leurs prédécesseurs pour des types spécifiques de charges de travail. D’autres nouveaux types dans .NET Framework 4, par exemple, System.Threading.Barrier et System.Threading.SpinLock, fournissent des fonctionnalités qui n’ont pas été disponibles dans les versions antérieures. Pour plus d’informations, consultez Structures de données pour la programmation parallèle.
Types de tâches personnalisés
Nous vous recommandons de ne pas hériter de System.Threading.Tasks.Task ou de System.Threading.Tasks.Task<TResult>. Au lieu de cela, nous vous recommandons d’utiliser la propriété AsyncState pour associer des données ou un état supplémentaires à un objet Task ou Task<TResult>. Vous pouvez également utiliser des méthodes d’extension pour étendre les fonctionnalités des classes Task et Task<TResult>. Pour plus d’informations sur les méthodes d’extension, consultez méthodes d’extension et méthodes d’extension.
Si vous devez hériter de Task ou de Task<TResult>, vous ne pouvez pas utiliser Run ou les System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>ou System.Threading.Tasks.TaskCompletionSource<TResult> classes pour créer des instances de votre type de tâche personnalisé. Vous ne pouvez pas les utiliser, car ces classes créent uniquement des objets Task et Task<TResult>. En outre, vous ne pouvez pas utiliser les mécanismes de continuation de tâche fournis par Task, Task<TResult>, TaskFactoryet TaskFactory<TResult> pour créer des instances de votre type de tâche personnalisé. Vous ne pouvez pas les utiliser, car ces classes créent également uniquement des objets Task et Task<TResult>.
Sections connexes
Titre | Description |
---|---|
Chaînage des tâches à l’aide de tâches de continuation | Décrit le fonctionnement des continuations. |
Tâches enfants attachées et détachées | Décrit la différence entre les tâches enfants attachées et les tâches enfants détachées. |
Annulation de la tâche | Décrit la prise en charge de l’annulation intégrée dans l’objet Task. |
gestion des exceptions | Décrit comment les exceptions sur les threads simultanés sont gérées. |
Comment : utiliser Parallel.Invoke pour exécuter des opérations parallèles | Décrit comment utiliser Invoke. |
Comment retourner une valeur d'une tâche | Décrit comment rapporter des valeurs à partir de tâches. |
Comment : annuler une tâche et ses enfants | Décrit comment annuler des tâches. |
Comment créer des tâches pré-calculées | Décrit comment utiliser la méthode Task.FromResult pour récupérer les résultats des opérations de téléchargement asynchrones conservées dans un cache. |
Guide pratique pour parcourir une arborescence binaire avec des tâches parallèles | Décrit comment utiliser des tâches pour parcourir une arborescence binaire. |
Comment déballer une tâche imbriquée | Montre comment utiliser la méthode d’extension Unwrap. |
le parallélisme des données | Décrit comment utiliser For et ForEach pour créer des boucles parallèles sur des données. |
Programmation Parallèle | Nœud de niveau supérieur pour la programmation parallèle .NET Framework. |