Cómo construir su propio programador de tareas en C #

La TPL (Task Parallel Library) es una de las características nuevas más interesantes en las versiones recientes de .NET Framework, que se introdujo por primera vez en .NET Framework 4.0. Para trabajar con el TPL, debería aprovechar el espacio de nombres System.Threading.Tasks.

¿Qué son los programadores de tareas? ¿Por qué los necesitamos?

Ahora bien, ¿cómo es que se programan las tareas? Bueno, hay un componente llamado programador de tareas que es responsable de programar sus tareas. En esencia, es una abstracción para un objeto de bajo nivel que puede poner en cola sus tareas en subprocesos.

.NET Framework le proporciona dos programadores de tareas. Estos incluyen el programador de tareas predeterminado que se ejecuta en el grupo de subprocesos del marco .NET y otro programador de tareas que se ejecuta en el contexto de sincronización de un destino específico. Tenga en cuenta que el programador de tareas predeterminado de TPL aprovecha el grupo de subprocesos de .NET Framework. Este grupo de subprocesos está a su vez representado por la clase ThreadPool que está contenida dentro del espacio de nombres System.Threading.Tasks.

Aunque el programador de tareas predeterminado será suficiente la mayor parte del tiempo, es posible que desee crear su propio programador de tareas personalizado para proporcionar funcionalidades adicionales, es decir, características que no proporciona el programador de tareas predeterminado. Tales características pueden incluir, ejecución FIFO, grado de concurrencia, etc.

Extienda la clase TaskScheduler en C #

Para construir su propio programador de tareas personalizado, necesitaría crear una clase que amplíe la clase System.Threading.Tasks.TaskScheduler. Por lo tanto, para construir un programador de tareas personalizado, necesitaría extender la clase abstracta TaskScheduler y anular los siguientes métodos.

  • QueueTask devuelve void y acepta un objeto Task como parámetro y este método se llama cuando se va a programar una tarea
  • GetScheduledTasks devuelve una lista (un IEnumerable para ser precisos) de todas las tareas que se han programado
  • TryExecuteTaskInline se utiliza para ejecutar tareas en línea, es decir, en el hilo actual. En este caso, las tareas se ejecutan sin la necesidad de ponerlas en cola.

El siguiente fragmento de código muestra cómo puede extender la clase TaskScheduler para implementar su programador personalizado en C #.

clase pública CustomTaskScheduler: TaskScheduler, IDisposable

    {

    }

Como comentamos anteriormente en este artículo, deberá anular los métodos GetScheduledTasks, QueueTask y TryExecuteTaskInline en el programador de tareas personalizado.

CustomTaskScheduler de clase sellada pública: TaskScheduler, IDisposable

  {

        protegido anular IEnumerable GetScheduledTasks ()

        {

            //QUE HACER

        }

        protegido anular void QueueTask (tarea)

        {

             //QUE HACER

        }

        protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)

        {

            //QUE HACER

        }

        public void Dispose ()

        {

            //QUE HACER

        }

  }

Use BlockingCollection para almacenar una colección de objetos de tarea en C #

Comencemos ahora a implementar nuestro programador de tareas personalizado. El siguiente fragmento de código muestra cómo puede aprovechar BlockingCollection para almacenar una colección de objetos de tarea.

CustomTaskScheduler de clase sellada pública: TaskScheduler, IDisposable

 {

        private BlockingCollection tasksCollection = new BlockingCollection ();

        hilo privado de solo lectura mainThread = null;

        public CustomTaskScheduler ()

        {

            mainThread = new Thread (nuevo ThreadStart (Ejecutar));

            si (! mainThread.IsAlive)

            {

                mainThread.Start ();

            }

        }

        vacío privado Ejecutar ()

        {

            foreach (tarea var en tasksCollection.GetConsumingEnumerable ())

            {

                TryExecuteTask (tarea);

            }

        } 

      //Otros metodos

  }

Consulte el constructor de la clase CustomTaskScheduler. Observe cómo se ha creado un nuevo hilo y cómo se ha comenzado a ejecutar el método Execute.

Implementar los métodos GetScheduledTasks, QueueTask y TryExecuteTaskInline en C #

A continuación, debemos implementar los tres métodos que debemos anular en nuestro programador de tareas personalizado. Estos tres métodos incluyen GetScheduledTasks, QueueTask y TryExecuteTaskInline.

El método GetScheduledTasks devuelve la instancia de la colección de tareas como IEnumerable. Esto se usa para que pueda enumerar la colección como se muestra en el método Execute. El método QueueTask acepta un objeto Task como parámetro y lo almacena en la colección de tareas. El método TryExecuteTaskInline no tiene una implementación; dejaré que el lector lo implemente.

protegido anular IEnumerable GetScheduledTasks ()

        {

            return tasksCollection.ToArray ();

        }

        protegido anular void QueueTask (tarea)

        {

            si (tarea! = nulo)

                tasksCollection.Add (tarea);

        }

        protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)

        {

            falso retorno;

        }

Ejemplo completo de CustomTaskScheduler en C #

La siguiente lista de códigos ilustra la versión final de nuestro CustomTaskScheduler.

CustomTaskScheduler de clase sellada pública: TaskScheduler, IDisposable

    {

        private BlockingCollection tasksCollection = new BlockingCollection ();

        hilo privado de solo lectura mainThread = null;

        public CustomTaskScheduler ()

        {

            mainThread = new Thread (nuevo ThreadStart (Ejecutar));

            si (! mainThread.IsAlive)

            {

                mainThread.Start ();

            }

        }

        vacío privado Ejecutar ()

        {

            foreach (tarea var en tasksCollection.GetConsumingEnumerable ())

            {

                TryExecuteTask (tarea);

            }

        }

        protegido anular IEnumerable GetScheduledTasks ()

        {

            return tasksCollection.ToArray ();

        }

        protegido anular void QueueTask (tarea)

        {

            si (tarea! = nulo)

                tasksCollection.Add (tarea);           

        }

        protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)

        {

            falso retorno;

        }

        Eliminación de vacío privado (eliminación de bool)

        {

            si (! desechando) regreso;

            tasksCollection.CompleteAdding ();

            tasksCollection.Dispose ();

        }

        public void Dispose ()

        {

            Desechar (verdadero);

            GC.SuppressFinalize (esto);

        }

    }

Para usar el programador de tareas personalizado que acabamos de implementar, puede usar el siguiente fragmento de código:

CustomTaskScheduler taskScheduler = new CustomTaskScheduler ();

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

Cómo hacer más en C #:

  • Cuándo usar una clase abstracta frente a una interfaz en C #
  • Cómo trabajar con AutoMapper en C #
  • Cómo usar expresiones lambda en C #
  • Cómo trabajar con delegados Action, Func y Predicate en C #
  • Cómo trabajar con delegados en C #
  • Cómo implementar un registrador simple en C #
  • Cómo trabajar con atributos en C #
  • Cómo trabajar con log4net en C #
  • Cómo implementar el patrón de diseño del repositorio en C #
  • Cómo trabajar con la reflexión en C #
  • Cómo trabajar con filesystemwatcher en C #
  • Cómo realizar la inicialización diferida en C #
  • Cómo trabajar con MSM en C #
  • Cómo trabajar con métodos de extensión en C #
  • Cómo usar expresiones lambda en C #
  • Cuándo usar la palabra clave volátil en C #
  • Cómo usar la palabra clave yield en C #
  • Cómo implementar polimorfismo en C #
  • Cómo construir su propio programador de tareas en C #
  • Cómo trabajar con RabbitM en C #
  • Cómo trabajar con una tupla en C #
  • Explorando métodos virtuales y abstractos en C #