Task启动方式及源码探究
启动Task有几种方式:
1.Task.Run()
2.new TaskFactory.StartNew()
3.var t=new Task(); t.start();
平时用的最多是第一和第二种,那么他们之间有什么差异?接下来通过两个demo进行说明。
static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Run(() => Sayhi("task")); new TaskFactory().StartNew(() => Sayhi("taskfactory")); } Console.ReadLine(); } public static void Sayhi(string method) { Console.WriteLine(method+":"+Thread.CurrentThread.ManagedThreadId); }
运行结果:
task:5
taskfactory:4
taskfactory:6
task:7
task:5
task:6
taskfactory:7
taskfactory:5
task:4
taskfactory:6
通过以上结果可以猜测task跟taskfactory运行的模式基本一致,线程重复使用,猜测是通过线程池来调度。
接下来,我给TaskFactory().StartNew添加一个参数TaskCreationOptions.LongRunning
new TaskFactory().StartNew(() => Sayhi("taskfactory"),TaskCreationOptions.LongRunning);
运行结果:
task:9
task:9
task:4
taskfactory:5
task:6
task:7
taskfactory:8
taskfactory:10
taskfactory:11
taskfactory:12
如果你眼神犀利,相信已经看出其中的差异。没错,taskfactory每次运行都是一个新的线程,由此可以猜测TaskCreationOptions.LongRunning这个参数会决定task运行的命运,它将不会进入线程池,自己单飞了。
综上所述,有以下猜测:
1.Task.Run()与TaskFactory().StartNew()运行模式一致
2.都是线程池调度
3.TaskCreationOptions=TaskCreationOptions.LongRunning时,不由线程池调度。
为了验证猜测,让我们来翻一下源码。
Task.Run()
public static Task Run(Action action) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default,
TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark); }
new TaskFactory().StartNew()
public Task StartNew(Action action) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; Task currTask = Task.InternalCurrent; return Task.InternalStartNew(currTask, action, null, m_defaultCancellationToken, GetDefaultScheduler(currTask), m_defaultCreationOptions, InternalTaskOptions.None, ref stackMark); }
可以看到,无论是Task.Run()还是new TaskFactory().StartNew()都是调用了Task.InternalStartNew方法,而Task.Run是默认的参数,TaskFactory().StartNew()拥有更多的自由度。由此验证猜测1。
继续跳进,最终调用ScheduleAndStart方法
internal void ScheduleAndStart(bool needsProtection) { try { // Queue to the indicated scheduler. m_taskScheduler.InternalQueueTask(this); //调度任务 } catch (ThreadAbortException tae) { AddException(tae); FinishThreadAbortedTask(true, false); } }
m_taskScheduler实例就是上面的TaskScheduler.Default,来瞧瞧是装了什么东西
private static readonly TaskScheduler s_defaultTaskScheduler = new ThreadPoolTaskScheduler(); public static TaskScheduler Default { get { return s_defaultTaskScheduler; } }
ThreadPoolTaskScheduler,瞧瞧这单词是不是有内味了,来,答案快水落石出了。
protected internal override void QueueTask(Task task) { if ((task.Options & TaskCreationOptions.LongRunning) != 0) { // Run LongRunning tasks on their own dedicated thread. Thread thread = new Thread(s_longRunningThreadWork); thread.IsBackground = true; // Keep this thread from blocking process shutdown thread.Start(task); } else { // Normal handling for non-LongRunning tasks. bool forceToGlobalQueue = ((task.Options & TaskCreationOptions.PreferFairness) != 0); ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue); } }
看到这里相信不需要我再说什么了... 猜测验证成功。
总结以下:
1.Task.Run()是TaskFactory().StartNew()的其中一种形式。TaskFactory().StartNew()拥有更多自由度。
2.Task.Run()是线程池调度,TaskFactory().StartNew()参数TaskCreationOptions=TaskCreationOptions.LongRunning时,不由线程池调度。
源码链接:https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs