Comment créer votre propre planificateur de tâches en C #

La TPL (Task Parallel Library) est l'une des nouvelles fonctionnalités les plus intéressantes des versions récentes de .NET Framework, ayant été introduite pour la première fois dans .NET Framework 4.0. Pour travailler avec le TPL, vous devez tirer parti de l'espace de noms System.Threading.Tasks.

Que sont les planificateurs de tâches? Pourquoi avons-nous besoin d'eux?

Maintenant, comment les tâches sont-elles planifiées? Eh bien, il existe un composant appelé planificateur de tâches qui est responsable de la planification de vos tâches. Il s'agit essentiellement d'une abstraction pour un objet de bas niveau qui peut mettre vos tâches en file d'attente sur des threads.

Le .NET Framework vous fournit deux planificateurs de tâches. Il s'agit notamment du planificateur de tâches par défaut qui s'exécute sur le pool de threads .NET Framework et d'un autre planificateur de tâches qui s'exécute sur le contexte de synchronisation d'une cible spécifiée. Notez que le planificateur de tâches par défaut du TPL tire parti du pool de threads .NET Framework. Ce pool de threads est à son tour représenté par la classe ThreadPool contenue dans l'espace de noms System.Threading.Tasks.

Bien que le planificateur de tâches par défaut suffise la plupart du temps, vous pouvez créer votre propre planificateur de tâches personnalisé pour fournir des fonctionnalités supplémentaires, c'est-à-dire des fonctionnalités qui ne sont pas fournies par le planificateur de tâches par défaut. Ces fonctionnalités peuvent inclure, l'exécution FIFO, le degré de concurrence, etc.

Étendre la classe TaskScheduler en C #

Pour créer votre propre planificateur de tâches personnalisé, vous devez créer une classe qui étend la classe System.Threading.Tasks.TaskScheduler. Ainsi, pour créer un planificateur de tâches personnalisé, vous devez étendre la classe abstraite TaskScheduler et remplacer les méthodes suivantes.

  • QueueTask retourne void et accepte un objet Task comme paramètre et cette méthode est appelée lorsqu'une tâche doit être planifiée
  • GetScheduledTasks renvoie une liste (un IEnumerable pour être précis) de toutes les tâches qui ont été planifiées
  • TryExecuteTaskInline est utilisé pour exécuter des tâches en ligne, c'est-à-dire sur le thread courant. Dans ce cas, les tâches sont exécutées sans qu'il soit nécessaire de les mettre en file d'attente

L'extrait de code suivant montre comment vous pouvez étendre la classe TaskScheduler pour implémenter votre planificateur personnalisé en C #.

classe publique CustomTaskScheduler: TaskScheduler, IDisposable

    {

    }

Comme nous l'avons vu précédemment dans cet article, vous devez remplacer les méthodes GetScheduledTasks, QueueTask et TryExecuteTaskInline dans le planificateur de tâches personnalisé.

classe scellée publique CustomTaskScheduler: TaskScheduler, IDisposable

  {

        remplacement protégé IEnumerable GetScheduledTasks ()

        {

            //FAIRE

        }

        protected override void QueueTask (tâche de tâche)

        {

             //FAIRE

        }

        protected override bool TryExecuteTaskInline (tâche de tâche, tâche booléenneWasPreviouslyQueued)

        {

            //FAIRE

        }

        public void Dispose ()

        {

            //FAIRE

        }

  }

Utilisez BlockingCollection pour stocker une collection d'objets de tâche en C #

Commençons maintenant à implémenter notre planificateur de tâches personnalisé. L'extrait de code suivant montre comment vous pouvez tirer parti de BlockingCollection pour stocker une collection d'objets de tâche.

classe scellée publique CustomTaskScheduler: TaskScheduler, IDisposable

 {

        private BlockingCollection tasksCollection = new BlockingCollection ();

        thread privé en lecture seule mainThread = null;

        public CustomTaskScheduler ()

        {

            mainThread = nouveau Thread (nouveau ThreadStart (Execute));

            if (! mainThread.IsAlive)

            {

                mainThread.Start ();

            }

        }

        private void Exécuter ()

        {

            foreach (tâche var dans tasksCollection.GetConsumingEnumerable ())

            {

                TryExecuteTask (tâche);

            }

        } 

      //Autres méthodes

  }

Reportez-vous au constructeur de la classe CustomTaskScheduler. Notez comment un nouveau thread a été créé et a commencé à exécuter la méthode Execute.

Implémentez les méthodes GetScheduledTasks, QueueTask et TryExecuteTaskInline en C #

Ensuite, nous devons implémenter les trois méthodes que nous devons remplacer dans notre planificateur de tâches personnalisé. Ces trois méthodes incluent GetScheduledTasks, QueueTask et TryExecuteTaskInline.

La méthode GetScheduledTasks renvoie l'instance de la collection de tâches en tant que IEnumerable. Ceci est utilisé afin que vous puissiez énumérer la collection comme indiqué dans la méthode Execute. La méthode QueueTask accepte un objet Task en tant que paramètre et le stocke dans la collection de tâches. La méthode TryExecuteTaskInline n'a pas d'implémentation - je laisserai au lecteur le soin de l'implémenter.

remplacement protégé IEnumerable GetScheduledTasks ()

        {

            return tasksCollection.ToArray ();

        }

        protected override void QueueTask (tâche de tâche)

        {

            if (tâche! = null)

                tâchesCollection.Add (tâche);

        }

        protected override bool TryExecuteTaskInline (tâche de tâche, tâche booléenneWasPreviouslyQueued)

        {

            retourner faux;

        }

Exemple complet de CustomTaskScheduler en C #

La liste de codes suivante illustre la version finale de notre CustomTaskScheduler.

classe scellée publique CustomTaskScheduler: TaskScheduler, IDisposable

    {

        private BlockingCollection tasksCollection = new BlockingCollection ();

        thread privé en lecture seule mainThread = null;

        public CustomTaskScheduler ()

        {

            mainThread = nouveau Thread (nouveau ThreadStart (Execute));

            if (! mainThread.IsAlive)

            {

                mainThread.Start ();

            }

        }

        private void Exécuter ()

        {

            foreach (tâche var dans tasksCollection.GetConsumingEnumerable ())

            {

                TryExecuteTask (tâche);

            }

        }

        remplacement protégé IEnumerable GetScheduledTasks ()

        {

            return tasksCollection.ToArray ();

        }

        protected override void QueueTask (tâche de tâche)

        {

            if (tâche! = null)

                tâchesCollection.Add (tâche);           

        }

        protected override bool TryExecuteTaskInline (tâche de tâche, tâche booléenneWasPreviouslyQueued)

        {

            retourner faux;

        }

        private void Dispose (suppression des booléens)

        {

            if (! disposition) return;

            tâchesCollection.CompleteAdding ();

            tâchesCollection.Dispose ();

        }

        public void Dispose ()

        {

            Dispose (vrai);

            GC.SuppressFinalize (ceci);

        }

    }

Pour utiliser le planificateur de tâches personnalisé que nous venons d'implémenter, vous pouvez utiliser l'extrait de code suivant:

CustomTaskScheduler taskScheduler = nouveau CustomTaskScheduler ();

Task.Factory.StartNew (() => SomeMethod (), CancellationToken.None, TaskCreationOptions.None, taskScheduler);

Comment faire plus en C #:

  • Quand utiliser une classe abstraite ou une interface en C #
  • Comment travailler avec AutoMapper en C #
  • Comment utiliser les expressions lambda en C #
  • Comment travailler avec les délégués Action, Func et Predicate en C #
  • Comment travailler avec des délégués en C #
  • Comment implémenter un simple logger en C #
  • Comment travailler avec des attributs en C #
  • Comment travailler avec log4net en C #
  • Comment implémenter le modèle de conception de référentiel en C #
  • Comment travailler avec la réflexion en C #
  • Comment travailler avec Filesystemwatcher en C #
  • Comment effectuer une initialisation différée en C #
  • Comment travailler avec MSM en C #
  • Comment travailler avec des méthodes d'extension en C #
  • Comment utiliser les expressions lambda en C #
  • Quand utiliser le mot clé volatile en C #
  • Comment utiliser le mot clé yield en C #
  • Comment implémenter le polymorphisme en C #
  • Comment créer votre propre planificateur de tâches en C #
  • Comment travailler avec RabbitM en C #
  • Comment travailler avec un tuple en C #
  • Explorer les méthodes virtuelles et abstraites en C #