Come creare il proprio pianificatore di attività in C #

La TPL (Task Parallel Library) è una delle novità più interessanti nelle recenti versioni di .NET framework, essendo stata introdotta per la prima volta in .NET Framework 4.0. Per lavorare con TPL è necessario sfruttare lo spazio dei nomi System.Threading.Tasks.

Cosa sono gli scheduler delle attività? Perché ne abbiamo bisogno?

Ora, come sono programmate le attività? Bene, c'è un componente chiamato task scheduler che è responsabile della pianificazione delle tue attività. In sostanza, è un'astrazione per un oggetto di basso livello che può accodare le tue attività sui thread.

.NET Framework fornisce due utilità di pianificazione delle attività. Questi includono l'utilità di pianificazione predefinita che viene eseguita nel pool di thread di .NET Framework e un'altra pianificazione delle attività che viene eseguita nel contesto di sincronizzazione di una destinazione specificata. Si noti che l'utilità di pianificazione predefinita del TPL sfrutta il pool di thread di .NET Framework. Questo pool di thread è a sua volta rappresentato dalla classe ThreadPool contenuta nello spazio dei nomi System.Threading.Tasks.

Sebbene il programma di pianificazione delle attività predefinito sarà sufficiente per la maggior parte del tempo, potresti voler creare il tuo pianificatore di attività personalizzato per fornire funzionalità aggiuntive, ad esempio caratteristiche che non sono fornite dal pianificatore di attività predefinito. Tali funzionalità possono includere, l'esecuzione FIFO, il grado di concorrenza, ecc.

Estendi la classe TaskScheduler in C #

Per creare il proprio pianificatore di attività personalizzato è necessario creare una classe che estenda la classe System.Threading.Tasks.TaskScheduler. Quindi, per creare un'utilità di pianificazione delle attività personalizzata, è necessario estendere la classe astratta TaskScheduler e sovrascrivere i seguenti metodi.

  • QueueTask restituisce void e accetta un oggetto Task come parametro e questo metodo viene chiamato quando un'attività deve essere pianificata
  • GetScheduledTasks restituisce un elenco (un IEnumerable per la precisione) di tutte le attività che sono state pianificate
  • TryExecuteTaskInline viene utilizzato per eseguire attività inline, ovvero sul thread corrente. In questo caso, le attività vengono eseguite senza la necessità di metterle in coda

Il frammento di codice seguente mostra come estendere la classe TaskScheduler per implementare l'utilità di pianificazione personalizzata in C #.

classe pubblica CustomTaskScheduler: TaskScheduler, IDisposable

    {

    }

Come discusso in precedenza in questo articolo, sarà necessario sostituire i metodi GetScheduledTasks, QueueTask e TryExecuteTaskInline nell'utilità di pianificazione personalizzata.

classe pubblica sigillata CustomTaskScheduler: TaskScheduler, IDisposable

  {

        override protetto IEnumerable GetScheduledTasks ()

        {

            //FARE

        }

        protected override void QueueTask (task task)

        {

             //FARE

        }

        protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)

        {

            //FARE

        }

        public void Dispose ()

        {

            //FARE

        }

  }

Usare BlockingCollection per archiviare una raccolta di oggetti attività in C #

Iniziamo ora a implementare il nostro pianificatore di attività personalizzato. Il frammento di codice seguente mostra come sfruttare BlockingCollection per archiviare una raccolta di oggetti attività.

classe pubblica sigillata CustomTaskScheduler: TaskScheduler, IDisposable

 {

        private BlockingCollection tasksCollection = new BlockingCollection ();

        private readonly Thread mainThread = null;

        CustomTaskScheduler pubblico ()

        {

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

            if (! mainThread.IsAlive)

            {

                mainThread.Start ();

            }

        }

        private void Execute ()

        {

            foreach (var task in tasksCollection.GetConsumingEnumerable ())

            {

                TryExecuteTask (attività);

            }

        } 

      // Altri metodi

  }

Fare riferimento al costruttore della classe CustomTaskScheduler. Notare come è stato creato un nuovo thread e avviato per eseguire il metodo Execute.

Implementare i metodi GetScheduledTasks, QueueTask e TryExecuteTaskInline in C #

Successivamente, dobbiamo implementare i tre metodi che dobbiamo sovrascrivere nel nostro pianificatore di attività personalizzato. Questi tre metodi includono GetScheduledTasks, QueueTask e TryExecuteTaskInline.

Il metodo GetScheduledTasks restituisce l'istanza della raccolta di attività come IEnumerable. Viene utilizzato in modo da poter enumerare la raccolta come mostrato nel metodo Execute. Il metodo QueueTask accetta un oggetto Task come parametro e lo memorizza nella raccolta di attività. Il metodo TryExecuteTaskInline non ha un'implementazione: lascerò che sia il lettore a implementarlo.

override protetto IEnumerable GetScheduledTasks ()

        {

            return tasksCollection.ToArray ();

        }

        protected override void QueueTask (task task)

        {

            if (task! = null)

                taskCollection.Add (task);

        }

        protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)

        {

            return false;

        }

Esempio di CustomTaskScheduler completo in C #

Il seguente listato di codice illustra la versione finale del nostro CustomTaskScheduler.

classe pubblica sigillata CustomTaskScheduler: TaskScheduler, IDisposable

    {

        private BlockingCollection tasksCollection = new BlockingCollection ();

        private readonly Thread mainThread = null;

        CustomTaskScheduler pubblico ()

        {

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

            if (! mainThread.IsAlive)

            {

                mainThread.Start ();

            }

        }

        private void Execute ()

        {

            foreach (var task in tasksCollection.GetConsumingEnumerable ())

            {

                TryExecuteTask (attività);

            }

        }

        override protetto IEnumerable GetScheduledTasks ()

        {

            return tasksCollection.ToArray ();

        }

        protected override void QueueTask (task task)

        {

            if (task! = null)

                taskCollection.Add (task);           

        }

        protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)

        {

            return false;

        }

        private void Dispose (bool disposing)

        {

            se (! disporre) restituire;

            taskCollection.CompleteAdding ();

            taskCollection.Dispose ();

        }

        public void Dispose ()

        {

            Dispose (true);

            GC.SuppressFinalize (this);

        }

    }

Per utilizzare l'utilità di pianificazione personalizzata che abbiamo appena implementato, puoi utilizzare il seguente frammento di codice:

CustomTaskScheduler taskScheduler = nuovo CustomTaskScheduler ();

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

Come fare di più in C #:

  • Quando usare una classe astratta e un'interfaccia in C #
  • Come lavorare con AutoMapper in C #
  • Come usare le espressioni lambda in C #
  • Come lavorare con i delegati Action, Func e Predicate in C #
  • Come lavorare con i delegati in C #
  • Come implementare un semplice logger in C #
  • Come lavorare con gli attributi in C #
  • Come lavorare con log4net in C #
  • Come implementare il modello di progettazione del repository in C #
  • Come lavorare con la riflessione in C #
  • Come lavorare con Filesystemwatcher in C #
  • Come eseguire l'inizializzazione pigra in C #
  • Come lavorare con MSM in C #
  • Come lavorare con i metodi di estensione in C #
  • Come utilizzare espressioni lambda in C #
  • Quando usare la parola chiave volatile in C #
  • Come utilizzare la parola chiave yield in C #
  • Come implementare il polimorfismo in C #
  • Come creare il proprio pianificatore di attività in C #
  • Come lavorare con RabbitM in C #
  • Come lavorare con una tupla in C #
  • Esplorazione di metodi virtuali e astratti in C #