zoukankan      html  css  js  c++  java
  • 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

     
  • 相关阅读:
    [LintCode] Cosine Similarity 余弦公式
    Word 2010 给公式添加序号
    Xshell连接不上虚拟机的问题和解决办法
    关于 “VMware Workstation 不可恢复错误- (vcpu-0)”
    TortoiseGit客户端安装及使用(上传代码到git@osc
    Android Studio修改项目名和包名
    Android 环信(Android)设置头像和昵称的方法
    Android SharedPreferences存储map的方法
    Android 环信聊天头像昵称显示解决方案
    Android 判断当前Fragment是否可见(Visible)
  • 原文地址:https://www.cnblogs.com/sosoeasy/p/12738183.html
Copyright © 2011-2022 走看看