.net Task底层原理探究
- 自从
.Net Framework 4.0
增加了Task
之后,日常开发中的多线程需求,几乎都会用Task
功能,那么Task
究竟是什么?底层运行原理是什么?今天就来深挖一下。 - 整个
Task.cs
文件,有7000多行代码,可见其复杂性
创建Task的几种方式
-
直接创建
Task
var task = new Task(()=>{});
-
Task.Run(()=>{})
- 其实就是 直接创建Task,然后调用
ScheduleAndStart
方法,采用默认的TaskScheduler
也就是在线程池中运行。
- 其实就是 直接创建Task,然后调用
-
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.Run
和Task.Factory.StartNew
,但其实后两种方式都是对于直接创建Task的封装,直接创建Task有以下两种好处- 可以分离创建任务和启动任务,后两种方式都是创建任务后直接启动
- 控制性更强,自定义化更高,你可以自行选择传入
TaskScheduler
和TaskCreationOptions
参数。(更推荐使用Task.Factory.StartNew
)
Task.Run
- 底层是调用
InternalStartNew(action, TaskScheduler.Default, TaskCreationOptions.DenyChildAttach)
TaskScheduler.Default
默认为ThreadPoolTaskScheduler
- 其实就是 直接创建Task,然后调用
ScheduleAndStart
方法,采用默认的TaskScheduler
也就是在线程池中运行。
Task.StartNew
- 和
Task.Run
类似,底层也是调用InternalStartNew
,但是可以自行传入TaskScheduler
和TaskCreationOptions
参数
TaskCreationOptions
的作用
-
使用场景举例:轮询线程,一般它的生命周期和应用程序的生命周期相同,不合适使用线程池线程,这种场景下创建Task的时候传入参数
TaskCreationOptions.LongRunning
就会直接new Thread
,而不是使用线程池线程Task.Factory.StartNew(Polling, TaskCreationOptions.LongRunning)
-
TaskCreationOptions
还有其他的一些不常用的值,这里就不作深入了解。 -
具体代码可以看下方的
ThreadPoolTaskScheduler.cs
自定义 TaskScheduler
-
创建Task的两个重要参数:
TaskScheduler
andTaskCreationOptions
,其中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);
}
}
}