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 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos