Cet article a fait l’objet d’une traduction automatique. Pour afficher l’article en anglais, activez la case d’option Anglais. Vous pouvez également afficher le texte anglais dans une fenêtre contextuelle en faisant glisser le pointeur de la souris sur le texte traduit.
Traduction
Anglais

TaskScheduler classe

 

Date de publication : juillet 2016

Représente un objet qui gère les tâches de bas niveau de la mise en file d'attente de tâches sur des threads.

Espace de noms:   System.Threading.Tasks
Assembly:  mscorlib (dans mscorlib.dll)

System.Object
  System.Threading.Tasks.TaskScheduler

[HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true, 
	ExternalThreading = true)]
[PermissionSetAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)]
public abstract class TaskScheduler

NomDescription
System_CAPS_protmethodTaskScheduler()

Initialise la TaskScheduler.

NomDescription
System_CAPS_pubpropertySystem_CAPS_staticCurrent

Obtient le TaskScheduler associé à la tâche en cours d’exécution.

System_CAPS_pubpropertySystem_CAPS_staticDefault

Obtient la valeur par défaut TaskScheduler instance fournie par le .NET Framework.

System_CAPS_pubpropertyId

Obtient l’ID unique pour ce TaskScheduler.

System_CAPS_pubpropertyMaximumConcurrencyLevel

Indique le niveau d’accès concurrentiel maximal TaskScheduler peut prendre en charge.

NomDescription
System_CAPS_pubmethodEquals(Object)

Détermine si l'objet spécifié est identique à l'objet actuel.(Hérité de Object.)

System_CAPS_protmethodFinalize()

Autorise un objet à tenter de libérer des ressources et d'exécuter d'autres opérations de nettoyage avant qu'il ne soit récupéré par l'opération garbage collection. (Hérité de Object.)

System_CAPS_pubmethodSystem_CAPS_staticFromCurrentSynchronizationContext()

Crée un TaskScheduler associé au System.Threading.SynchronizationContext en cours.

System_CAPS_pubmethodGetHashCode()

Fait office de fonction de hachage par défaut.(Hérité de Object.)

System_CAPS_protmethodGetScheduledTasks()

Débogueur prise en charge uniquement, génère un énumérable de Task instances actuellement en file d’attente sur le planificateur en attente doit être exécuté.

System_CAPS_pubmethodGetType()

Obtient le Type de l'instance actuelle.(Hérité de Object.)

System_CAPS_protmethodMemberwiseClone()

Crée une copie superficielle du Object actuel.(Hérité de Object.)

System_CAPS_protmethodQueueTask(Task)

Files d’attente un Task le planificateur.

System_CAPS_pubmethodToString()

Retourne une chaîne qui représente l'objet actuel.(Hérité de Object.)

System_CAPS_protmethodTryDequeue(Task)

Tente de la file d’attente un Task qui a été précédemment en attente pour ce planificateur.

System_CAPS_protmethodTryExecuteTask(Task)

Tente d’exécuter les Task sur ce planificateur.

System_CAPS_protmethodTryExecuteTaskInline(Task, Boolean)

Détermine si les Task peut être exécutée de façon synchrone dans cet appel et si c’est possible, il s’exécute.

NomDescription
System_CAPS_pubeventSystem_CAPS_staticUnobservedTaskException

Se produit lorsque l’exception non prise en charge d’une tâche ayant échoué est sur le point de déclencher la stratégie de promotion d’exception, qui, par défaut, arrête le processus.

Une instance de la TaskScheduler classe représente un planificateur de tâches. Un planificateur de tâches s’assure que le travail d’une tâche est finalement exécuté.

Le planificateur de tâches par défaut est basé sur le pool de threads .NET Framework 4, qui fournit le vol de travail pour l'équilibrage de charge, l'injection/retrait du thread pour un débit maximal et une bonne performance globale. Ce 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 les tâches planifiées comment doit être exposée aux débogueurs. Si vous avez besoin des fonctionnalités spéciales, vous pouvez créer un planificateur personnalisé et l’activer pour les requêtes ou des tâches spécifiques.

Dans cette rubrique :

le Planificateur de tâches par défaut et le pool de threads

la file d’attente globale et files d’attente locales
vol de travail
tâches longues
incorporation de tâche

spécifiant un contexte de synchronisation                         

Le planificateur par défaut pour la bibliothèque parallèle de tâches et PLINQ utilise le pool de threads .NET Framework, qui est représenté par le ThreadPool (classe), à la file d’attente et les exécuter. Le pool de threads utilise les informations fournies par le Task type à prendre en charge efficacement le parallélisme affiné (unités éphémères de travail) et les tâches parallèles requêtes souvent représentent.

Le pool de threads gère une globale FIFO (premier entré, premier sorti) fonctionnent de la file d’attente pour les threads dans chaque domaine d’application. Chaque fois qu’un programme appelle la ThreadPool.QueueUserWorkItem (ou ThreadPool.UnsafeQueueUserWorkItem) méthode, le travail est placé sur cette file d’attente partagée et finalement annuler en file d’attente vers le thread suivant qui devient disponible. À compter de .NET Framework 4, cette file d’attente a été améliorée pour utiliser un algorithme sans verrou qui ressemble à la ConcurrentQueue<T> classe. À l’aide de cette implémentation sans verrou, le pool de threads passe moins de temps lorsqu’il met et sort file d’attente des éléments de travail. Ce gain 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 mises en file d’attente globale comme tout 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 dans une file d'attente locale qui est spécifique au thread sur lequel s'exécute la tâche parente. La tâche parente peut être une tâche de niveau supérieur ou l'enfant d'une autre tâche. Quand ce thread est prêt pour exécuter davantage de travail, il regarde en premier dans la file d'attente locale. Les éléments de travail qui s’y trouvent éventuellement peuvent être récupérés rapidement. Les files d’attente locales sont accessibles par ordre dernier entré, entré premier sorti (LIFO) pour conserver la localité du cache et de réduire la contention. Pour plus d’informations sur les tâches enfants et imbriquées, consultez Attached and Detached Child Tasks.

L’utilisation de files d’attente locales non seulement réduit la pression sur la file d’attente globale, mais également tire parti de la localité des données. Éléments de travail locaux file d’attente fréquemment des structures de données de référence qui sont physiquement près d’un autre dans la mémoire. Dans ces cas, les données sont déjà dans le cache après que la première tâche s’est exécutée et sont accessibles rapidement. Les deux Parallel LINQ (PLINQ) et Parallel largement de classe utilisent des tâches imbriquées et tâches enfants et accomplissent des accélérations significatives à l’aide de files d’attente de travail locales.

À compter de .NET Framework 4, le pool de threads propose également un algorithme de vol de travail pour vous assurer qu’aucun thread ne soit inactif alors que d’autres ont du travail dans leurs files d’attente. Quand un thread de pool de threads est prêt à effectuer davantage de travail, il regarde successivement en tête de sa file d'attente locale, dans la file d'attente globale, puis dans 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 méthodes heuristiques pour s’assurer qu’il peut exécuter le travail efficacement. Si c’est possible, il sort file d’attente l’élément de travail à partir de la fin (dans l’ordre FIFO). Cela réduit les conflits sur chaque file d'attente locale et permet de conserver la localité des données. Cette architecture permet le thread pool équilibrer la charge de travail plus efficacement que les versions antérieures.

Vous pouvez explicitement empêcher une tâche d’être mise en 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 TaskCreationOptions.LongRunning, qui indique au planificateur qu'un thread supplémentaire peut être nécessaire pour la tâche afin qu'elle n'entrave pas la progression d'autres threads ou éléments de travail sur la file d'attente locale. À l’aide de cette option vous évitez le pool de threads complètement, d’inclure les files d’attente locales et globales.

Dans certains cas, quand un Task est attendue, elle peut être exécutée de façon 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 à la place à l’aide du thread existant, ce qui se serait bloqué dans le cas contraire. Pour éviter les erreurs dues à la réentrance, incorporation de tâche se produit uniquement lorsque la cible d’attente se trouve dans la file d’attente de locale du thread pertinent.

Vous pouvez utiliser la méthode TaskScheduler.FromCurrentSynchronizationContext pour spécifier qu'une tâche doit être planifiée pour fonctionner sur un thread particulier. Cela s'avère utile dans les infrastructures telles que Windows Forms et Windows Presentation Foundation où l'accès aux objets d'interface utilisateur est souvent restreint au code qui s'exécute sur le thread sur lequel l'objet d'interface utilisateur a été créé. Pour plus d’informations, consultez Comment : planifier le travail sur le Thread d’Interface utilisateur (IU).

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 le contrôle d’interface utilisateur utilisateur a été créé. L’exemple crée une mosaïque d’images qui sont sélectionnées au hasard à partir d’un répertoire spécifié. Les objets WPF sont utilisés pour charger et redimensionner les images. Les pixels bruts sont ensuite passées à une tâche qui utilise un For boucle pour écrire les données de pixel dans un grand tableau sur un octet. Aucune synchronisation n’est requise, car aucun 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 mosaïque. Le tableau de grande taille est ensuite transmis à une tâche qui s’exécute sur le thread d’interface utilisateur, où les données de pixel sont chargées dans un contrôle Image.

L’exemple déplace les données du thread d’interface utilisateur, il modifie à l’aide de boucles parallèles et Task objets, puis le 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 pris 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ôler et ajouter des images. Le WrapPanel gère les tâches de positionnement des mosaïques. Toutefois, ce travail peut uniquement être effectué 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
            // 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éer un projet d’application WPF dans Visual Studio et lui attribuer un nom de votre choix. Procédez comme suit :

  1. En mode conception, faites glisser un Image contrôle depuis la boîte à outils à l’aire de conception. Dans la vue XAML, spécifiez l’alignement horizontal comme « Left ». La taille n’a pas d’importance, car le contrôle est redimensionné dynamiquement au moment de l’exécution. Acceptez le nom par défaut, « image ».

  2. Faites glisser un Button contrôle depuis la boîte à outils à la partie inférieure gauche de la fenêtre d’application. Double-cliquez sur le bouton pour ajouter un Click Gestionnaire d’événements. Dans la vue XAML, spécifiez la Content propriété du bouton comme « une mosaïque » et spécifiez son alignement horizontal comme « Left ». Acceptez le nom par défaut, « bouton ».

  3. Remplacez le contenu entier du fichier MainWindow.xaml.cs ou MainWindow.xaml.vb par le code de cet exemple. Assurez-vous que le nom de l’espace de travail correspond au nom du projet.

  4. L’exemple lit les images JPEG à partir d’un répertoire nommé C:\Users\Public\Pictures\Sample Pictures\. Créer le répertoire et placer des images qu’il contient ou modifier le chemin d’accès pour faire référence à un autre annuaire qui contient des images.

Cet exemple présente certaines limitations. Par exemple, les images d’uniquement 32-bits par pixel sont prises en charge ; images dans d’autres formats sont endommagées par le BitmapImage l’objet au cours de l’opération de redimensionnement. En outre, les images de la source doivent tous être supérieure à la taille de la vignette. En guise d’exercice supplémentaire, vous pouvez ajouter la fonctionnalité permettant de gérer plusieurs formats de pixel et les tailles des fichiers.

L’exemple suivant provient de la Samples for Parallel Programming with the .NET Framework 4 sur le site Web MSDN Code Gallery. Il crée un planificateur de tâches qui limite le nombre de threads utilisés par l’application. Ensuite, il lance 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  

In addition, several sample task schedulers are available on Code Gallery: Samples for Parallel Programming with the .NET Framework 4http://go.microsoft.com/fwlink/?LinkID=165717.

Plateforme Windows universelle
Disponible depuis 8
.NET Framework
Disponible depuis 4.0
Bibliothèque de classes portable
Pris en charge dans : plateformes .NET portables
Silverlight
Disponible depuis 5.0
Silverlight pour Windows Phone
Disponible depuis 8.0
Windows Phone
Disponible depuis 8.1

Tous les membres du résumé TaskScheduler type sont thread-safe et peuvent être utilisés à partir de plusieurs threads simultanément.

Retour au début
Afficher: