.net Task底层原理探究

  • 自从 .Net Framework 4.0 增加了 Task 之后,日常开发中的多线程需求,几乎都会用 Task 功能,那么 Task 究竟是什么?底层运行原理是什么?今天就来深挖一下。
  • 整个 Task.cs 文件,有7000多行代码,可见其复杂性

创建Task的几种方式

  • 直接创建 Task

    • var task = new Task(()=>{});
  • Task.Run(()=>{})

    • 其实就是 直接创建Task,然后调用 ScheduleAndStart 方法,采用默认的 TaskScheduler 也就是在线程池中运行。
  • Task.Factory.StartNew(()=>{})

  • Paraller.For(0, 100, (i) => {})

  • Paraller.Invoke(()=>{}, ()=>{})

  • Enumerable.Range(0, 1).AsParaller().ForAll(i => {})

直接创建Task

var task = new Task(()=>{});
task.Start();
  • 日常开发中很少用到这种方式,一般使用 Task.RunTask.Factory.StartNew,但其实后两种方式都是对于直接创建Task的封装,直接创建Task有以下两种好处
    • 可以分离创建任务和启动任务,后两种方式都是创建任务后直接启动
    • 控制性更强,自定义化更高,你可以自行选择传入 TaskSchedulerTaskCreationOptions 参数。(更推荐使用 Task.Factory.StartNew

Task.Run

  • 底层是调用 InternalStartNew(action, TaskScheduler.Default, TaskCreationOptions.DenyChildAttach)
    • TaskScheduler.Default 默认为 ThreadPoolTaskScheduler
  • 其实就是 直接创建Task,然后调用 ScheduleAndStart 方法,采用默认的 TaskScheduler 也就是在线程池中运行。

Task.StartNew

  • Task.Run类似,底层也是调用 InternalStartNew ,但是可以自行传入 TaskSchedulerTaskCreationOptions 参数

TaskCreationOptions 的作用

  • 使用场景举例:轮询线程,一般它的生命周期和应用程序的生命周期相同,不合适使用线程池线程,这种场景下创建Task的时候传入参数 TaskCreationOptions.LongRunning 就会直接 new Thread ,而不是使用线程池线程

    • Task.Factory.StartNew(Polling, TaskCreationOptions.LongRunning)
  • TaskCreationOptions 还有其他的一些不常用的值,这里就不作深入了解。

  • 具体代码可以看下方的 ThreadPoolTaskScheduler.cs

自定义 TaskScheduler

  • 创建Task的两个重要参数: TaskScheduler and TaskCreationOptions,其中 TaskCreationOptions 上面已经讲过,接下来再来研究下 TaskScheduler

  • 可以理解为它的默认参数是 TaskScheduler.Default

  • 接下来自定义 TaskScheduler,让创建的线程使用 new Thread 方式

 Task.Factory.StartNew(() =>
 {

 }, new CancellationToken(), TaskCreationOptions.LongRunning, new CustomTaskScheduler());

public class CustomTaskScheduler : 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();
    }
}

Task简化版源码

  • Task.cs
public class Task
{
    private TaskScheduler m_taskScheduler; // The task scheduler this task runs under.
    private Delegate? m_action;
    private object? m_stateObject; // A state object that can be optionally supplied, passed to action.
    internal Task(Delegate action, object? stateObj, TaskScheduler taskScheduler, TaskCreationOptions taskCreationOptions)
    {
        m_action = action;
        m_taskScheduler = taskScheduler;
    }
    
    public void Start()
    {
        Start(TaskScheduler.Current);
    }
    
    public void Start(TaskScheduler scheduler)
    {
        m_taskScheduler = scheduler;
        ScheduleAndStart(true);
    }
    
    public void ScheduleAndStart(bool needsProtection)
    {
        m_taskScheduler.InternalQueueTask(this);
    }
    
    public static Task Run(Action action)
    {
         // TaskScheduler.Default is ThreadPoolTaskScheduler
        return InternalStartNew(action, TaskScheduler.Default, TaskCreationOptions.DenyChildAttach);
    }
    
    internal static Task InternalStartNew(Delegate action, TaskScheduler scheduler, TaskCreationOptions options)
    {
         Task t = new Task(action, null, scheduler, options);  
         t.ScheduleAndStart(false);
        return t;
    }
}
  • ThreadPoolTaskScheduler.cs
public class ThreadPoolTaskScheduler: TaskScheduler
{
    private static readonly ParameterizedThreadStart s_longRunningThreadWork = static s =>
	{
    	((Task)s).ExecuteEntryUnsafe(threadPoolThread: null);
	};

    internal override void QueueTask(Task task)
    {
        TaskCreationOptions options = task.Options;
        // 如果是长期运行,不进线程池,直接创建Thread并启动
    	if ( (options & TaskCreationOptions.LongRunning) != 0)
        {
            new Thread(s_longRunningThreadWork){
                IsBackground = true,
                Name = ".NET Long Running Task"
            }.UnsafeStart(task);
        }
        // 进入线程池
        else 
        {
            // 如果是 PreferFairness 就是 Local,否则就是Global
        	ThreadPool.UnsafeQueueUserWorkItemInternal(task, (options & TaskCreationOptions.PreferFairness) == 0);    
        }
    }
}
posted on 2025-04-09 17:45  baby-jie  阅读(43)  评论(0)    收藏  举报