Take advantage of a custom task scheduler to provide added functionalities over the default task scheduler and manage how tasks are scheduled in .Net Credit: inspirexpressmiami The TPL (Task Parallel Library) is one of the most interesting new features in the recent versions of .NET framework, having first been introduced in .NET Framework 4.0. To work with the TPL you would need to take advantage of the System.Threading.Tasks namespace. What are task schedulers? Why do we need them? Now, how is that the tasks are scheduled? Well, there is a component called task scheduler that is responsible for scheduling your tasks. In essence, it’s an abstraction for a low-level object that can queue your tasks onto threads. The .NET Framework provides you with two task schedulers. These include the default task scheduler that runs on the .NET framework thread pool, and another task scheduler that executes on the synchronization context of a specified target. Note that the default task scheduler of the TPL takes advantage of the .NET Framework thread pool. This thread pool is in turn represented by the ThreadPool class that is contained inside the System.Threading.Tasks namespace. Although the default task scheduler will suffice most of the time, you may want to build your own custom task scheduler to provide added functionalities, i.e. features that are not provided by the default task scheduler. Such features may include, FIFO execution, degree of concurrency, etc. Extend the TaskScheduler class in C# To build your own custom task scheduler you would need to create a class that extends the System.Threading.Tasks.TaskScheduler class. So, to build a custom task scheduler, you would need to extend the TaskScheduler abstract class and override the following methods. QueueTask returns void and accepts a Task object as parameter and this method is called when a task is to be scheduled GetScheduledTasks returns a list (an IEnumerable to be precise) of all the tasks that have been scheduled TryExecuteTaskInline is used to execute tasks inline, i.e., on the current thread. In this case, the tasks are executed sans the need of queuing them The following code snippet shows how you can extend the TaskScheduler class to implement your custom scheduler in C#. public class CustomTaskScheduler : TaskScheduler, IDisposable { } As we discussed earlier in this article, you would need to override the GetScheduledTasks, QueueTask, and TryExecuteTaskInline methods in the custom task scheduler. public sealed class CustomTaskScheduler : TaskScheduler, IDisposable { protected override IEnumerable<Task> GetScheduledTasks() { //TODO } protected override void QueueTask(Task task) { //TODO } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { //TODO } public void Dispose() { //TODO } } Use BlockingCollection to store a collection of task objects in C# Let’s now start implementing our custom task scheduler. The following code snippet shows how you can leverage BlockingCollection to store a collection of task objects. public sealed class CustomTaskScheduler : TaskScheduler, IDisposable { private BlockingCollection<Task> tasksCollection = new BlockingCollection<Task>(); private readonly Thread mainThread = null; public CustomTaskScheduler() { mainThread = new Thread(new ThreadStart(Execute)); if (!mainThread.IsAlive) { mainThread.Start(); } } private void Execute() { foreach (var task in tasksCollection.GetConsumingEnumerable()) { TryExecuteTask(task); } } //Other methods } Refer to the constructor of the CustomTaskScheduler class. Note how a new thread has been created and started to run the Execute method. Implement the GetScheduledTasks, QueueTask, and TryExecuteTaskInline methods in C# Next, we need to implement the three methods that we need to override in our custom task scheduler. These three methods include the GetScheduledTasks, QueueTask, and TryExecuteTaskInline. The GetScheduledTasks method returns the instance of the task collection as IEnumerable. This is used so that you can enumerate the collection as shown in the Execute method. The QueueTask method accepts a Task object as a parameter and stores it in the task collection. The TryExecuteTaskInline method doesn’t have an implementation — I will leave it to the reader to implement it. protected override IEnumerable<Task> GetScheduledTasks() { return tasksCollection.ToArray(); } protected override void QueueTask(Task task) { if (task != null) tasksCollection.Add(task); } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return false; } Complete CustomTaskScheduler example in C# The following code listing illustrates the final version of our CustomTaskScheduler. public sealed class CustomTaskScheduler : TaskScheduler, IDisposable { private BlockingCollection<Task> tasksCollection = new BlockingCollection<Task>(); private readonly Thread mainThread = null; public CustomTaskScheduler() { mainThread = new Thread(new ThreadStart(Execute)); if (!mainThread.IsAlive) { mainThread.Start(); } } private void Execute() { foreach (var task in tasksCollection.GetConsumingEnumerable()) { TryExecuteTask(task); } } protected override IEnumerable<Task> GetScheduledTasks() { return tasksCollection.ToArray(); } protected override void QueueTask(Task task) { if (task != null) tasksCollection.Add(task); } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return false; } private void Dispose(bool disposing) { if (!disposing) return; tasksCollection.CompleteAdding(); tasksCollection.Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } To use the custom task scheduler we just implemented, you can use the following code snippet: CustomTaskScheduler taskScheduler = new CustomTaskScheduler(); Task.Factory.StartNew(() => SomeMethod(), CancellationToken.None, TaskCreationOptions.None, taskScheduler); How to do more in C#: When to use an abstract class vs. interface in C# How to work with AutoMapper in C# How to use lambda expressions in C# How to work with Action, Func, and Predicate delegates in C# How to work with delegates in C# How to implement a simple logger in C# How to work with attributes in C# How to work with log4net in C# How to implement the repository design pattern in C# How to work with reflection in C# How to work with filesystemwatcher in C# How to perform lazy initialization in C# How to work with MSM in C# How to work with extension methods in C# How to us lambda expressions in C# When to use the volatile keyword in C# How to use the yield keyword in C# How to implement polymorphism in C# How to build your own task scheduler in C# How to work with RabbitM in C# How to work with a tuple in C# Exploring virtual and abstract methods in C# Related content feature What is Rust? Safe, fast, and easy software development Unlike most programming languages, Rust doesn't make you choose between speed, safety, and ease of use. Find out how Rust delivers better code with fewer compromises, and a few downsides to consider before learning Rust. By Serdar Yegulalp Nov 20, 2024 11 mins Rust Programming Languages Software Development how-to Kotlin for Java developers: Classes and coroutines Kotlin was designed to bring more flexibility and flow to programming in the JVM. Here's an in-depth look at how Kotlin makes working with classes and objects easier and introduces coroutines to modernize concurrency. By Matthew Tyson Nov 20, 2024 9 mins Java Kotlin Programming Languages analysis Azure AI Foundry tools for changes in AI applications Microsoft’s launch of Azure AI Foundry at Ignite 2024 signals a welcome shift from chatbots to agents and to using AI for business process automation. By Simon Bisson Nov 20, 2024 7 mins Microsoft Azure Generative AI Development Tools news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks Resources Videos