Principle of C#TaskScheduler task scheduler

Posted by sandrine2411 on Wed, 26 Jan 2022 18:31:20 +0100

At ordinary times, when we develop with multithreading, we cannot do without tasks. Indeed, tasks have brought us great programming efficiency. There is a TaskScheduler at the bottom of the task, which determines how tasks should be scheduled, and

Yes There are two kinds of system defined schedulers in the. net framework. The first is the default ThreadPoolTaskScheduler for tasks, or the other is the SynchronizationContextTaskScheduler,

And how to customize other than these two types, this article just shares with you.

 

1: ThreadPoolTaskScheduler

This scheduler mechanism is the default mechanism of task, and it can be seen from the name that it is a mechanism of delegation to ThreadPool, which also shows that task is based on ThreadPool

Encapsulation, source code

 protected internal override void QueueTask(Task task)
        {
            TaskCreationOptions options = task.Options;
            if (Thread.IsThreadStartSupported && (options & TaskCreationOptions.LongRunning) != 0)
            {
                // Run LongRunning tasks on their own dedicated thread.
                new Thread(s_longRunningThreadWork)
                {
                    IsBackground = true,
                    Name = ".NET Long Running Task"
                }.UnsafeStart(task);
            }
            else
            {
                // Normal handling for non-LongRunning tasks.
                ThreadPool.UnsafeQueueUserWorkItemInternal(task, (options & TaskCreationOptions.PreferFairness) == 0);
            }
        }

From the above code, we can see the following logic. If the TaskCreationOptions on the current task is set to LongRunning, the task will be delegated to the Thread for execution

The benefits are obvious. If the long-running task occupies the Thread of ThreadPool, ThreadPool will open up some threads again to ensure sufficient threads. If the time-consuming task is released at this time,

It will lead to too many ThreadPool threads and frequent context switching, so it's a good choice to let the Task execute in the Thread. Of course, if you don't specify this long running, it's OK

It is executed on ThreadPool. If you don't believe it, you can also use windbg to verify...

 

static void Main(string[] args)
         {
             var task = Task.Factory.StartNew(() =>
             {
                 Console.WriteLine("hello world!!!");
             }, TaskCreationOptions.LongRunning);

             Console.Read();
         }

 

 

 

 

static void Main(string[] args)
         {
             var task = Task.Factory.StartNew(() =>
             {
                 Console.WriteLine("hello world!!!");
             });

             Console.Read();
         }

 

If you are not familiar with windbg, it doesn't matter. Let's not discuss it for the moment. We just remove the TaskCreationOptions enumeration and use this form! threads show you the difference

It should be very clear.

 

 

 

Well, when you see these two figures, you should understand that with LongRunning, there is no ThreadPool Worker mark in the thread, which indicates that it is a separate thread, and the following

This figure clearly bears this identification, which indicates that the delegate is currently executed in ThreadPool.

2: SynchronizationContextTaskScheduler

From this name, we can see that this is a taskscheduler that synchronizes the context. The principle is to throw the heavy and time-consuming work to the ThreadPool, and then throw the operation of updating the UI to the UI thread

In the queue, it is executed by the UIThread. You can also see some details in this scheduler.

1        protected internal override void QueueTask(Task task)
2          {
3              this.m_synchronizationContext.Post(SynchronizationContextTaskScheduler.s_postCallback, task);
4          }

Then you can start from S_ You can see an Invoke function in the postcallback, as shown in the following figure:

1  public virtual void Post(SendOrPostCallback d, object state)
2  {
3      ThreadPool.QueueUserWorkItem(new WaitCallback(d.Invoke), state);
4  }

With this foundation, let's take a look at how to write the code. We can see that the following code does not block the UIThread, which is perfect~~~

private void button1_Click(object sender, EventArgs e)
         {
             Task task = Task.Factory.StartNew(() =>
             {
                 //Complex operation, wait 10 s
                 Thread.Sleep(10000);

             }).ContinueWith((t) =>
             {
                 button1.Text = "hello world";
             }, TaskScheduler.FromCurrentSynchronizationContext());
         }

3: Customize TaskScheduler

We know that in the existing There are only two kinds of task schedulers in the. net framework. Some students may want to ask, these schedulers are uncomfortable for me to use. I want to customize them. This can be used

With? of course!!! If you want to customize, just customize a class to implement the TaskScheduler, and then you can simplify the ThreadPoolTaskScheduler, that is, I want to

All tasks need to use Thread and avoid using TheadPool. Is that ok? Of course, I don't believe you see.

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var task = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("hello world!!!");
            }, new CancellationToken(), TaskCreationOptions.None, new PerThreadTaskScheduler());

            Console.Read();
        }
    }

    /// <summary>
    /// each Task One Thread
    /// </summary>
    public class PerThreadTaskScheduler : TaskScheduler
    {
        protected override IEnumerable<Task> GetScheduledTasks()
        {
            return null;
        }

        protected override void QueueTask(Task task)
        {
            var thread = new Thread(() =>
            {
                TryExecuteTask(task);
            });

            thread.Start();
        }

        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            throw new NotImplementedException();
        }
    }
}

 

Topics: C#