TaskScheduler Classe
Définition
Important
Certaines informations portent sur la préversion du produit qui est susceptible d’être en grande partie modifiée avant sa publication. Microsoft exclut toute garantie, expresse ou implicite, concernant les informations fournies ici.
Représente un objet qui gère le travail de bas niveau des tâches de mise en file d’attente sur des threads.
public ref class TaskScheduler abstract
public abstract class TaskScheduler
type TaskScheduler = class
Public MustInherit Class TaskScheduler
- Héritage
-
TaskScheduler
Exemples
L’exemple suivant crée un planificateur de tâches personnalisé qui limite le nombre de threads utilisés par l’application. Il lance ensuite deux ensembles de tâches et affiche des informations sur la tâche et le thread sur lequel la tâche s’exécute.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
class Example
{
static void Main()
{
// Create a scheduler that uses two threads.
LimitedConcurrencyLevelTaskScheduler lcts = new LimitedConcurrencyLevelTaskScheduler(2);
List<Task> tasks = new List<Task>();
// Create a TaskFactory and pass it our custom scheduler.
TaskFactory factory = new TaskFactory(lcts);
CancellationTokenSource cts = new CancellationTokenSource();
// Use our factory to run a set of tasks.
Object lockObj = new Object();
int outputItem = 0;
for (int tCtr = 0; tCtr <= 4; tCtr++) {
int iteration = tCtr;
Task t = factory.StartNew(() => {
for (int i = 0; i < 1000; i++) {
lock (lockObj) {
Console.Write("{0} in task t-{1} on thread {2} ",
i, iteration, Thread.CurrentThread.ManagedThreadId);
outputItem++;
if (outputItem % 3 == 0)
Console.WriteLine();
}
}
}, cts.Token);
tasks.Add(t);
}
// Use it to run a second set of tasks.
for (int tCtr = 0; tCtr <= 4; tCtr++) {
int iteration = tCtr;
Task t1 = factory.StartNew(() => {
for (int outer = 0; outer <= 10; outer++) {
for (int i = 0x21; i <= 0x7E; i++) {
lock (lockObj) {
Console.Write("'{0}' in task t1-{1} on thread {2} ",
Convert.ToChar(i), iteration, Thread.CurrentThread.ManagedThreadId);
outputItem++;
if (outputItem % 3 == 0)
Console.WriteLine();
}
}
}
}, cts.Token);
tasks.Add(t1);
}
// Wait for the tasks to complete before displaying a completion message.
Task.WaitAll(tasks.ToArray());
cts.Dispose();
Console.WriteLine("\n\nSuccessful completion.");
}
}
// Provides a task scheduler that ensures a maximum concurrency level while
// running on top of the thread pool.
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
// Indicates whether the current thread is processing work items.
[ThreadStatic]
private static bool _currentThreadIsProcessingItems;
// The list of tasks to be executed
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)
// The maximum concurrency level allowed by this scheduler.
private readonly int _maxDegreeOfParallelism;
// Indicates whether the scheduler is currently processing work items.
private int _delegatesQueuedOrRunning = 0;
// Creates a new instance with the specified degree of parallelism.
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
{
if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
_maxDegreeOfParallelism = maxDegreeOfParallelism;
}
// Queues a task to the scheduler.
protected sealed override void QueueTask(Task task)
{
// Add the task to the list of tasks to be processed. If there aren't enough
// delegates currently queued or running to process tasks, schedule another.
lock (_tasks)
{
_tasks.AddLast(task);
if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
{
++_delegatesQueuedOrRunning;
NotifyThreadPoolOfPendingWork();
}
}
}
// Inform the ThreadPool that there's work to be executed for this scheduler.
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
_currentThreadIsProcessingItems = true;
try
{
// Process all available items in the queue.
while (true)
{
Task item;
lock (_tasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (_tasks.Count == 0)
{
--_delegatesQueuedOrRunning;
break;
}
// Get the next item from the queue
item = _tasks.First.Value;
_tasks.RemoveFirst();
}
// Execute the task we pulled out of the queue
base.TryExecuteTask(item);
}
}
// We're done processing items on the current thread
finally { _currentThreadIsProcessingItems = false; }
}, null);
}
// Attempts to execute the specified task on the current thread.
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!_currentThreadIsProcessingItems) return false;
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued)
// Try to run the task.
if (TryDequeue(task))
return base.TryExecuteTask(task);
else
return false;
else
return base.TryExecuteTask(task);
}
// Attempt to remove a previously scheduled task from the scheduler.
protected sealed override bool TryDequeue(Task task)
{
lock (_tasks) return _tasks.Remove(task);
}
// Gets the maximum concurrency level supported by this scheduler.
public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }
// Gets an enumerable of the tasks currently scheduled on this scheduler.
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_tasks, ref lockTaken);
if (lockTaken) return _tasks;
else throw new NotSupportedException();
}
finally
{
if (lockTaken) Monitor.Exit(_tasks);
}
}
}
// The following is a portion of the output from a single run of the example:
// 'T' in task t1-4 on thread 3 'U' in task t1-4 on thread 3 'V' in task t1-4 on thread 3
// 'W' in task t1-4 on thread 3 'X' in task t1-4 on thread 3 'Y' in task t1-4 on thread 3
// 'Z' in task t1-4 on thread 3 '[' in task t1-4 on thread 3 '\' in task t1-4 on thread 3
// ']' in task t1-4 on thread 3 '^' in task t1-4 on thread 3 '_' in task t1-4 on thread 3
// '`' in task t1-4 on thread 3 'a' in task t1-4 on thread 3 'b' in task t1-4 on thread 3
// 'c' in task t1-4 on thread 3 'd' in task t1-4 on thread 3 'e' in task t1-4 on thread 3
// 'f' in task t1-4 on thread 3 'g' in task t1-4 on thread 3 'h' in task t1-4 on thread 3
// 'i' in task t1-4 on thread 3 'j' in task t1-4 on thread 3 'k' in task t1-4 on thread 3
// 'l' in task t1-4 on thread 3 'm' in task t1-4 on thread 3 'n' in task t1-4 on thread 3
// 'o' in task t1-4 on thread 3 'p' in task t1-4 on thread 3 ']' in task t1-2 on thread 4
// '^' in task t1-2 on thread 4 '_' in task t1-2 on thread 4 '`' in task t1-2 on thread 4
// 'a' in task t1-2 on thread 4 'b' in task t1-2 on thread 4 'c' in task t1-2 on thread 4
// 'd' in task t1-2 on thread 4 'e' in task t1-2 on thread 4 'f' in task t1-2 on thread 4
// 'g' in task t1-2 on thread 4 'h' in task t1-2 on thread 4 'i' in task t1-2 on thread 4
// 'j' in task t1-2 on thread 4 'k' in task t1-2 on thread 4 'l' in task t1-2 on thread 4
// 'm' in task t1-2 on thread 4 'n' in task t1-2 on thread 4 'o' in task t1-2 on thread 4
// 'p' in task t1-2 on thread 4 'q' in task t1-2 on thread 4 'r' in task t1-2 on thread 4
// 's' in task t1-2 on thread 4 't' in task t1-2 on thread 4 'u' in task t1-2 on thread 4
// 'v' in task t1-2 on thread 4 'w' in task t1-2 on thread 4 'x' in task t1-2 on thread 4
// 'y' in task t1-2 on thread 4 'z' in task t1-2 on thread 4 '{' in task t1-2 on thread 4
// '|' in task t1-2 on thread 4 '}' in task t1-2 on thread 4 '~' in task t1-2 on thread 4
// 'q' in task t1-4 on thread 3 'r' in task t1-4 on thread 3 's' in task t1-4 on thread 3
// 't' in task t1-4 on thread 3 'u' in task t1-4 on thread 3 'v' in task t1-4 on thread 3
// 'w' in task t1-4 on thread 3 'x' in task t1-4 on thread 3 'y' in task t1-4 on thread 3
// 'z' in task t1-4 on thread 3 '{' in task t1-4 on thread 3 '|' in task t1-4 on thread 3
Remarques
La TaskScheduler classe représente un planificateur de tâches. Un planificateur de tâches garantit que le travail d’une tâche est finalement exécuté.
Le planificateur de tâches par défaut fournit le vol de travail pour l’équilibrage de charge, l’injection/mise hors service de threads pour un débit maximal et de bonnes performances globales. Il doit être suffisant pour la plupart des scénarios.
La TaskScheduler classe sert également de point d’extension pour toute logique de planification personnalisable. Cela inclut des mécanismes tels que la planification d’une tâche pour l’exécution et la façon dont les tâches planifiées doivent être exposées aux débogueurs. Si vous avez besoin de fonctionnalités spéciales, vous pouvez créer un planificateur personnalisé et l’activer pour des tâches ou des requêtes spécifiques.
Planificateur de tâches par défaut et pool de threads
Le planificateur par défaut de la Bibliothèque de tâches parallèles et de PLINQ utilise le pool de threads de .NET, représenté par la classe ThreadPool, pour mettre en file d’attente et exécuter le travail. Le pool de threads utilise les informations fournies par le Task type pour prendre en charge efficacement le parallélisme affiné (unités de travail de courte durée) que représentent souvent les tâches et requêtes parallèles.
File d’attente globale et files d’attente locales
Le pool de threads gère une file d’attente de travail FIFO globale (premier entré, premier sortie) pour les threads de chaque domaine d’application. Chaque fois qu’un programme appelle la méthode ThreadPool.QueueUserWorkItem (ou ThreadPool.UnsafeQueueUserWorkItem), le travail est placé dans cette file d’attente partagée et finalement retiré de la file d’attente vers le thread suivant qui devient disponible. À compter de .NET Framework 4, cette file d’attente utilise un algorithme sans verrou qui ressemble à la ConcurrentQueue<T> classe. Grâce à cette implémentation sans verrouillage, le pool de threads passe moins de temps à mettre en file d’attente et à retirer des éléments de travail. Cet avantage de performances est disponible pour tous les programmes qui utilisent le pool de threads.
Les tâches de niveau supérieur, qui ne sont pas créées dans le contexte d’une autre tâche, sont placées dans la file d’attente globale comme n’importe quel autre élément de travail. Toutefois, les tâches imbriquées ou enfants, créées dans le contexte d’une autre tâche, sont gérées tout à fait différemment. Une tâche enfant ou imbriquée est placée sur une file d’attente locale spécifique au thread sur lequel la tâche parente s’exécute. La tâche parente peut être une tâche de niveau supérieur ou l’enfant d’une autre tâche. Lorsque ce thread est prêt à effectuer plus de travail, il consulte d'abord la file d'attente locale. Si les éléments de travail y attendent, ils sont accessibles rapidement. Les files d'attente locales sont accessibles selon l'ordre dernier entré, premier sorti (LIFO) pour préserver la localité du cache et réduire la contention. Pour plus d’informations sur les tâches enfants et les tâches imbriquées, consultez Tâches enfants jointes et détachées.
L’utilisation de files d’attente locales réduit non seulement la pression sur la file d’attente globale, mais tire également parti de la localité des données. Les éléments de travail de la file d’attente locale font souvent référence à des structures de données physiquement proches les unes des autres en mémoire. Dans ce cas, les données se trouvent déjà dans le cache après l’exécution de la première tâche et sont accessibles rapidement. Parallel LINQ (PLINQ) et la Parallel classe utilisent largement les tâches imbriquées et les tâches enfants, et effectuent des accélérations significatives à l’aide des files d’attente de travail locales.
Vol de travail
À partir de .NET Framework 4, le pool de threads intègre également un algorithme de répartition du travail, garantissant qu'aucun thread ne reste inactif pendant que d'autres ont encore du travail dans leurs files d'attente. Lorsqu’un thread dans un pool de threads est prêt pour plus de travail, il examine d’abord le début de sa file d’attente locale, puis la file d’attente globale, puis les files d’attente locales d’autres threads. S’il trouve un élément de travail dans la file d’attente locale d’un autre thread, il applique d’abord des heuristiques pour s’assurer qu’il peut exécuter efficacement le travail. Si possible, il désenfile l’élément de travail à partir de la fin (dans l’ordre FIFO). Cela réduit la contention sur chaque file d’attente locale et conserve la localité des données. Cette architecture permet au pool de threads de mieux équilibrer la charge de travail qu'avec les versions précédentes.
Tâches longues
Vous pouvez empêcher explicitement une tâche d’être placée sur une file d’attente locale. Par exemple, vous savez peut-être qu’un élément de travail particulier fonctionnera pour une durée relativement longue et sera à même de bloquer tous les autres éléments de travail sur la file d’attente locale. Dans ce cas, vous pouvez spécifier l’option System.Threading.Tasks.TaskCreationOptions , qui fournit un indicateur au planificateur qu’un thread supplémentaire peut être requis pour la tâche afin qu’elle ne bloque pas la progression vers l’avant d’autres threads ou éléments de travail dans la file d’attente locale. En utilisant cette option, vous évitez complètement le pool de threads, y compris les files d’attente globales et locales.
Inlining des tâches
Dans certains cas, lorsqu’un Task est en attente, il peut être exécuté de manière synchrone sur le thread qui effectue l’opération d’attente. Cela améliore les performances en empêchant la nécessité d’un thread supplémentaire et en utilisant plutôt le thread existant, ce qui aurait été bloqué dans le cas contraire. Pour éviter les erreurs dues à la réentrance, l’inlining des tâches ne se produit que lorsque la cible d’attente se trouve dans la file d’attente locale du thread concerné.
Spécifier un contexte de synchronisation
Vous pouvez utiliser la TaskScheduler.FromCurrentSynchronizationContext méthode pour spécifier qu’une tâche doit être planifiée pour s’exécuter sur un thread particulier. Cela est utile dans les frameworks tels que Windows Forms et Windows Presentation Foundation, où l’accès aux objets d’interface utilisateur est souvent limité au code exécuté sur le même thread sur lequel l’objet d’interface utilisateur a été créé.
L’exemple suivant utilise la TaskScheduler.FromCurrentSynchronizationContext méthode dans une application Windows Presentation Foundation (WPF) pour planifier une tâche sur le même thread que celui sur lequel le contrôle d’interface utilisateur a été créé. L’exemple crée une mosaïque d’images sélectionnées de manière aléatoire à partir d’un répertoire spécifié. Les objets WPF sont utilisés pour charger et redimensionner les images. Les pixels bruts sont ensuite transmis à une tâche qui utilise une boucle For pour écrire les données de pixels dans un grand tableau à un seul octet. Aucune synchronisation n’est requise, car aucune deux vignettes n’occupent les mêmes éléments de tableau. Les vignettes peuvent également être écrites dans n’importe quel ordre, car leur position est calculée indépendamment de toute autre vignette. Le grand tableau est ensuite transmis à une tâche qui s’exécute sur le thread de l’interface utilisateur, où les données de pixels sont chargées dans un contrôle Image.
L’exemple déplace les données hors du thread d’interface utilisateur, les modifie à l’aide de boucles et Task d’objets parallèles, puis les transmet à une tâche qui s’exécute sur le thread d’interface utilisateur. Cette approche est utile lorsque vous devez utiliser la bibliothèque parallèle de tâches pour effectuer des opérations qui ne sont pas prises en charge par l’API WPF ou qui ne sont pas suffisamment rapides. Une autre façon de créer une mosaïque d’images dans WPF consiste à utiliser un System.Windows.Controls.WrapPanel contrôle et à y ajouter des images. WrapPanel gère le positionnement des mosaïques. Toutefois, ce travail ne peut être effectué que sur le thread d’interface utilisateur.
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WPF_CS1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private int fileCount;
int colCount;
int rowCount;
private int tilePixelHeight;
private int tilePixelWidth;
private int largeImagePixelHeight;
private int largeImagePixelWidth;
private int largeImageStride;
PixelFormat format;
BitmapPalette palette = null;
public MainWindow()
{
InitializeComponent();
// For this example, values are hard-coded to a mosaic of 8x8 tiles.
// Each tile is 50 pixels high and 66 pixels wide and 32 bits per pixel.
colCount = 12;
rowCount = 8;
tilePixelHeight = 50;
tilePixelWidth = 66;
largeImagePixelHeight = tilePixelHeight * rowCount;
largeImagePixelWidth = tilePixelWidth * colCount;
largeImageStride = largeImagePixelWidth * (32 / 8);
this.Width = largeImagePixelWidth + 40;
image.Width = largeImagePixelWidth;
image.Height = largeImagePixelHeight;
}
private void button_Click(object sender, RoutedEventArgs e)
{
// For best results use 1024 x 768 jpg files at 32bpp.
string[] files = System.IO.Directory.GetFiles(@"C:\Users\Public\Pictures\Sample Pictures\", "*.jpg");
fileCount = files.Length;
Task<byte[]>[] images = new Task<byte[]>[fileCount];
for (int i = 0; i < fileCount; i++)
{
int x = i;
images[x] = Task.Factory.StartNew(() => LoadImage(files[x]));
}
// When they've all been loaded, tile them into a single byte array.
var tiledImage = Task.Factory.ContinueWhenAll(
images, (i) => TileImages(i));
// We are currently on the UI thread. Save the sync context and pass it to
// the next task so that it can access the UI control "image".
var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();
// On the UI thread, put the bytes into a bitmap and
// display it in the Image control.
var t3 = tiledImage.ContinueWith((antecedent) =>
{
// Get System DPI.
Matrix m = PresentationSource.FromVisual(Application.Current.MainWindow)
.CompositionTarget.TransformToDevice;
double dpiX = m.M11;
double dpiY = m.M22;
BitmapSource bms = BitmapSource.Create(largeImagePixelWidth,
largeImagePixelHeight,
dpiX,
dpiY,
format,
palette, //use default palette
antecedent.Result,
largeImageStride);
image.Source = bms;
}, UISyncContext);
}
byte[] LoadImage(string filename)
{
// Use the WPF BitmapImage class to load and
// resize the bitmap. NOTE: Only 32bpp formats are supported correctly.
// Support for additional color formats is left as an exercise
// for the reader. For more information, see documentation for ColorConvertedBitmap.
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(filename);
bitmapImage.DecodePixelHeight = tilePixelHeight;
bitmapImage.DecodePixelWidth = tilePixelWidth;
bitmapImage.EndInit();
format = bitmapImage.Format;
int size = (int)(bitmapImage.Height * bitmapImage.Width);
int stride = (int)bitmapImage.Width * 4;
byte[] dest = new byte[stride * tilePixelHeight];
bitmapImage.CopyPixels(dest, stride, 0);
return dest;
}
int Stride(int pixelWidth, int bitsPerPixel)
{
return (((pixelWidth * bitsPerPixel + 31) / 32) * 4);
}
// Map the individual image tiles to the large image
// in parallel. Any kind of raw image manipulation can be
// done here because we are not attempting to access any
// WPF controls from multiple threads.
byte[] TileImages(Task<byte[]>[] sourceImages)
{
byte[] largeImage = new byte[largeImagePixelHeight * largeImageStride];
int tileImageStride = tilePixelWidth * 4; // hard coded to 32bpp
Random rand = new Random();
Parallel.For(0, rowCount * colCount, (i) =>
{
// Pick one of the images at random for this tile.
int cur = rand.Next(0, sourceImages.Length);
byte[] pixels = sourceImages[cur].Result;
// Get the starting index for this tile.
int row = i / colCount;
int col = (int)(i % colCount);
int idx = ((row * (largeImageStride * tilePixelHeight)) + (col * tileImageStride));
// Write the pixels for the current tile. The pixels are not contiguous
// in the array, therefore we have to advance the index by the image stride
// (minus the stride of the tile) for each scanline of the tile.
int tileImageIndex = 0;
for (int j = 0; j < tilePixelHeight; j++)
{
// Write the next scanline for this tile.
for (int k = 0; k < tileImageStride; k++)
{
largeImage[idx++] = pixels[tileImageIndex++];
}
// Advance to the beginning of the next scanline.
idx += largeImageStride - tileImageStride;
}
});
return largeImage;
}
}
}
Pour créer l’exemple, créez un projet d’application WPF dans Visual Studio et nommez-le WPF_CS1 (pour un projet WPF C#) ou WPF_VB1 (pour un projet WPF Visual Basic). Faites ensuite ce qui suit :
Dans la vue Conception, faites glisser un contrôle Image de la boîte à outils vers le coin supérieur gauche de la surface de conception. Dans la zone de texte Name de la fenêtre Propriétés , nommez le contrôle « image ».
Faites glisser un contrôle Button de la boîte à outils vers la partie inférieure gauche de la fenêtre de l’application. En mode XAML, spécifiez la Content propriété du bouton comme « Créer une mosaïque » et spécifiez sa Width propriété comme « 100 ». Connectez l’événement Click avec le
button_Clickgestionnaire d’événements défini dans le code de l’exemple en ajoutantClick="button_Click"à l’élément<Button>. Dans la zone de texte Nom de la fenêtre Propriétés , nommez le contrôle « button ».Remplacez tout le contenu du fichier MainWindow.xaml.cs ou MainWindow.xaml.vb par le code de cet exemple. Pour un projet WPF C#, vérifiez que le nom de l’espace de travail correspond au nom du projet.
L’exemple lit des images JPEG à partir d’un répertoire nommé C :\Users\Public\Pictures\Sample Pictures. Créez le répertoire et placez-y certaines images, ou modifiez le chemin d’accès pour faire référence à un autre répertoire qui contient des images.
Cet exemple présente certaines limitations. Par exemple, seules les images de 32 bits par pixel sont prises en charge ; les images dans d’autres formats sont endommagées par l’objet BitmapImage pendant l’opération de redimensionnement. De plus, les images sources doivent toutes être plus grandes que la taille de la mosaïque. En guise d’exercice supplémentaire, vous pouvez ajouter des fonctionnalités pour gérer plusieurs formats de pixels et tailles de fichier.
Constructeurs
| Nom | Description |
|---|---|
| TaskScheduler() |
Initialise la TaskScheduler. |
Propriétés
| Nom | Description |
|---|---|
| Current |
Obtient l’associé TaskScheduler à la tâche en cours d’exécution. |
| Default |
Obtient l’instance par défaut TaskScheduler fournie par .NET. |
| Id |
Obtient l’ID unique de ce TaskScheduler. |
| MaximumConcurrencyLevel |
Indique le niveau TaskScheduler d’accès concurrentiel maximal qui est en mesure de prendre en charge. |
Méthodes
| Nom | Description |
|---|---|
| Equals(Object) |
Détermine si l’objet spécifié est égal à l’objet actuel. (Hérité de Object) |
| Finalize() |
Libère toutes les ressources associées à ce planificateur. |
| FromCurrentSynchronizationContext() |
Crée un TaskScheduler associé au fichier actif SynchronizationContext. |
| GetHashCode() |
Sert de fonction de hachage par défaut. (Hérité de Object) |
| GetScheduledTasks() |
Pour la prise en charge du débogueur uniquement, génère une énumérable d’instances Task actuellement mises en file d’attente vers le planificateur en attente d’exécution. |
| GetType() |
Obtient la Type de l’instance actuelle. (Hérité de Object) |
| MemberwiseClone() |
Crée une copie superficielle du Objectactuel. (Hérité de Object) |
| QueueTask(Task) |
Met en file d’attente un Task planificateur. |
| ToString() |
Retourne une chaîne qui représente l’objet actuel. (Hérité de Object) |
| TryDequeue(Task) |
Tente de mettre en file d’attente un Task fichier précédemment mis en file d’attente vers ce planificateur. |
| TryExecuteTask(Task) |
Tente d’exécuter l’élément fourni Task sur ce planificateur. |
| TryExecuteTaskInline(Task, Boolean) |
Détermine si l’élément fourni Task peut être exécuté de manière synchrone dans cet appel, et s’il le peut, l’exécute. |
Événements
| Nom | Description |
|---|---|
| UnobservedTaskException |
Se produit lorsqu’une exception non traitée d’une tâche défaillante est sur le point de déclencher la stratégie d’escalade d’exception, qui, par défaut, met fin au processus. |
S’applique à
Cohérence de thread
Tous les membres du type abstrait TaskScheduler sont thread-safe et peuvent être utilisés simultanément à partir de plusieurs threads.