.NET 4包含新名称空间System.Threading.Tasks,它 包含的类抽象出了线程功能, 在底层使用ThreadPool。 任务表示应完成的某个单元的工作。 这个单元的工作可以在单独的线程中运行,也可以以同步方式启动一个任务,这需要等待主调线程。 使用任务不仅可以获得一个抽象层,还可以对底层线程进行很多控制。   

  在安排需要完成的工作时,任务提供了非常大的灵活性。 例如,可 以定义连续的工 作—— 在一个任务完成后该执行什么工作。 这可以区分任务成功与否。 另外,还可以在层次结构中安排任务。例如,父任务可以创建新的子任务。 这可以创建一种依赖关系,这样,取消父任务,也会取消其子任务。

  asyn和awit,通过获取task句柄的方式,在适当的时候根据句柄回调,这种语法糖更方便的实现异步编程。

Task和Thread的区别

  • Task是架构在Thread之上的,也就是说任务最终还是要抛给线程去执行。
  • Task跟Thread不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。

Task控制台测试程序

  1.  
    using System;
  2.  
    using System.Collections.Generic;
  3.  
    using System.Linq;
  4.  
    using System.Text;
  5.  
    using System.Threading;
  6.  
    using System.Threading.Tasks;
  7.  
     
  8.  
    namespace MyTask
  9.  
    {
  10.  
    class Program
  11.  
    {
  12.  
    /// <summary>
  13.  
    /// 启动方式
  14.  
    /// </summary>
  15.  
    public static void StartWay()
  16.  
    {
  17.  
    var task1 = new Task(() =>
  18.  
    {
  19.  
    Console.WriteLine("Hello,task");
  20.  
    });
  21.  
    task1.Start();
  22.  
     
  23.  
    var task2 = Task.Factory.StartNew(() =>
  24.  
    {
  25.  
    Console.WriteLine("Hello,task started by task factory");
  26.  
    });
  27.  
    }
  28.  
     
  29.  
    /// <summary>
  30.  
    /// 生命周期测试
  31.  
    /// </summary>
  32.  
    public static void LifeCircle()
  33.  
    {
  34.  
    var task1 = new Task(() =>
  35.  
    {
  36.  
    Console.WriteLine("Begin");
  37.  
    System.Threading.Thread.Sleep(2000);
  38.  
    Console.WriteLine("Finish");
  39.  
    });
  40.  
    Console.WriteLine("Before start:" + task1.Status);
  41.  
    task1.Start();
  42.  
    Console.WriteLine("After start:" + task1.Status);
  43.  
    task1.Wait();
  44.  
    Console.WriteLine("After Finish:" + task1.Status);
  45.  
     
  46.  
    }
  47.  
     
  48.  
    /// <summary>
  49.  
    /// 等待所有任务完成
  50.  
    /// </summary>
  51.  
    public static void WaitAll()
  52.  
    {
  53.  
    var task1 = new Task(() =>
  54.  
    {
  55.  
    Console.WriteLine("Task 1 Begin");
  56.  
    System.Threading.Thread.Sleep(2000);
  57.  
    Console.WriteLine("Task 1 Finish");
  58.  
    });
  59.  
    var task2 = new Task(() =>
  60.  
    {
  61.  
    Console.WriteLine("Task 2 Begin");
  62.  
    System.Threading.Thread.Sleep(3000);
  63.  
    Console.WriteLine("Task 2 Finish");
  64.  
    });
  65.  
     
  66.  
    task1.Start();
  67.  
    task2.Start();
  68.  
    Task.WaitAll(task1, task2);
  69.  
    Console.WriteLine("WaitAll task finished!");
  70.  
     
  71.  
    }
  72.  
     
  73.  
    /// <summary>
  74.  
    /// 等待任意一个执行任务完成
  75.  
    /// </summary>
  76.  
    public static void WaitAny()
  77.  
    {
  78.  
    var task1 = new Task(() =>
  79.  
    {
  80.  
    Console.WriteLine("Task 1 Begin");
  81.  
    System.Threading.Thread.Sleep(2000);
  82.  
    Console.WriteLine("Task 1 Finish");
  83.  
    });
  84.  
    var task2 = new Task(() =>
  85.  
    {
  86.  
    Console.WriteLine("Task 2 Begin");
  87.  
    System.Threading.Thread.Sleep(3000);
  88.  
    Console.WriteLine("Task 2 Finish");
  89.  
    });
  90.  
     
  91.  
    task1.Start();
  92.  
    task2.Start();
  93.  
    Task.WaitAny(task1, task2);
  94.  
    Console.WriteLine("WaitAny task finished!");
  95.  
    }
  96.  
     
  97.  
    /// <summary>
  98.  
    /// 任务回调方法
  99.  
    /// </summary>
  100.  
    public static void ContinueWith()
  101.  
    {
  102.  
    var task1 = new Task(() =>
  103.  
    {
  104.  
    Console.WriteLine("Task 1 Begin");
  105.  
    System.Threading.Thread.Sleep(2000);
  106.  
    Console.WriteLine("Task 1 Finish");
  107.  
    });
  108.  
    var task2 = new Task(() =>
  109.  
    {
  110.  
    Console.WriteLine("Task 2 Begin");
  111.  
    System.Threading.Thread.Sleep(3000);
  112.  
    Console.WriteLine("Task 2 Finish");
  113.  
    });
  114.  
     
  115.  
     
  116.  
    task1.Start();
  117.  
    task2.Start();
  118.  
    var result = task1.ContinueWith<string>(task =>
  119.  
    {
  120.  
    Console.WriteLine("task1 finished!");
  121.  
    return "This is task1 result!";
  122.  
    });
  123.  
     
  124.  
    Console.WriteLine(result.Result.ToString());
  125.  
     
  126.  
    var result2 = task2.ContinueWith<string>(task =>
  127.  
    {
  128.  
    Console.WriteLine("task2 finished!");
  129.  
    return "This is task2 result!";
  130.  
    });
  131.  
     
  132.  
    Console.WriteLine(result2.Result.ToString());
  133.  
    }
  134.  
     
  135.  
    /// <summary>
  136.  
    /// 取消任务
  137.  
    /// </summary>
  138.  
    public static void Cancel()
  139.  
    {
  140.  
    var tokenSource = new CancellationTokenSource();
  141.  
    var token = tokenSource.Token;
  142.  
    var task = Task.Factory.StartNew(() =>
  143.  
    {
  144.  
    for (var i = 0; i < 1000; i++)
  145.  
    {
  146.  
    System.Threading.Thread.Sleep(1000);
  147.  
    if (token.IsCancellationRequested)
  148.  
    {
  149.  
    Console.WriteLine("Abort mission success!");
  150.  
    return;
  151.  
    }
  152.  
    }
  153.  
    }, token);
  154.  
    token.Register(() =>
  155.  
    {
  156.  
    Console.WriteLine("Canceled");
  157.  
    });
  158.  
    Console.WriteLine("Press enter to cancel task...");
  159.  
    Console.ReadKey();
  160.  
    tokenSource.Cancel();
  161.  
     
  162.  
    Console.ReadKey();//这句忘了加,程序退出了,看不到“Abort mission success!“这个提示
  163.  
    }
  164.  
     
  165.  
    /// <summary>
  166.  
    /// 函数入口
  167.  
    /// </summary>
  168.  
    /// <param name="args"></param>
  169.  
    static void Main(string[] args)
  170.  
    {
  171.  
    Console.WriteLine("StartWay is runing");
  172.  
    StartWay();
  173.  
    Console.WriteLine("LifeCircle is runing");
  174.  
    LifeCircle();
  175.  
    Console.WriteLine("WaitAll is runing");
  176.  
    WaitAll();
  177.  
    Console.WriteLine("WaitAny is runing");
  178.  
    WaitAny();
  179.  
    Console.WriteLine("ContinueWith is runing");
  180.  
    ContinueWith();
  181.  
    Console.WriteLine("Cancel is runing");
  182.  
    Cancel();
  183.  
     
  184.  
    Console.Read();
  185.  
    }
  186.  
    }
  187.  
    }

参考地址:http://www.cnblogs.com/yunfeifei/p/4106318.html

内嵌和异常

  1.  
    using System;
  2.  
    using System.Collections.Generic;
  3.  
    using System.Linq;
  4.  
    using System.Text;
  5.  
    using System.Threading;
  6.  
    using System.Threading.Tasks;
  7.  
     
  8.  
    namespace MyTask
  9.  
    {
  10.  
    class Program
  11.  
    {
  12.  
     
  13.  
    /// <summary>
  14.  
    /// 内部Task测试-普通方式
  15.  
    /// <mark>
  16.  
    /// 执行结果:
  17.  
    /// Parent task finished!
  18.  
    /// Flag
  19.  
    /// Childen task finished!
  20.  
    /// </mark>
  21.  
    /// </summary>
  22.  
    public static void InnerTask()
  23.  
    {
  24.  
    var pTask = Task.Factory.StartNew(() =>
  25.  
    {
  26.  
    var cTask = Task.Factory.StartNew(() =>
  27.  
    {
  28.  
    System.Threading.Thread.Sleep(2000);
  29.  
    Console.WriteLine("Childen task finished!");
  30.  
    });
  31.  
    Console.WriteLine("Parent task finished!");
  32.  
    });
  33.  
    pTask.Wait();
  34.  
    Console.WriteLine("Flag");
  35.  
    }
  36.  
     
  37.  
    /// <summary>
  38.  
    /// 内部Task测试-父子任务关联
  39.  
    /// <mark>
  40.  
    /// 执行结果:
  41.  
    /// Parent task finished!
  42.  
    /// Childen task finished!
  43.  
    /// Flag
  44.  
    /// </mark>
  45.  
    /// </summary>
  46.  
    public static void InnerTaskAttachedToParent()
  47.  
    {
  48.  
    var pTask = Task.Factory.StartNew(() =>
  49.  
    {
  50.  
    var cTask = Task.Factory.StartNew(() =>
  51.  
    {
  52.  
    System.Threading.Thread.Sleep(2000);
  53.  
    Console.WriteLine("Childen task finished!");
  54.  
    }, TaskCreationOptions.AttachedToParent);
  55.  
    Console.WriteLine("Parent task finished!");
  56.  
    });
  57.  
    pTask.Wait();
  58.  
    Console.WriteLine("Flag");
  59.  
    }
  60.  
     
  61.  
    /// <summary>
  62.  
    /// 多任务互相关联结果运算
  63.  
    /// </summary>
  64.  
    public static void MultiInnerTask()
  65.  
    {
  66.  
    Task.Factory.StartNew(() =>
  67.  
    {
  68.  
    var t1 = Task.Factory.StartNew<int>(() =>
  69.  
    {
  70.  
    Console.WriteLine("Task 1 running...");
  71.  
    return 1;
  72.  
    });
  73.  
    t1.Wait(); //等待任务一完成
  74.  
    var t3 = Task.Factory.StartNew<int>(() =>
  75.  
    {
  76.  
    Console.WriteLine("Task 3 running...");
  77.  
    return t1.Result + 3;
  78.  
    });
  79.  
    var t4 = Task.Factory.StartNew<int>(() =>
  80.  
    {
  81.  
    Console.WriteLine("Task 2 running...");
  82.  
    return t1.Result + 2;
  83.  
    }).ContinueWith<int>(task =>
  84.  
    {
  85.  
    Console.WriteLine("Task 4 running...");
  86.  
    return task.Result + 4;
  87.  
    });
  88.  
    Task.WaitAll(t3, t4); //等待任务三和任务四完成
  89.  
    var result = Task.Factory.StartNew(() =>
  90.  
    {
  91.  
    Console.WriteLine("Task Finished! The result is {0}", t3.Result + t4.Result);
  92.  
    });
  93.  
    });
  94.  
    }
  95.  
     
  96.  
    /// <summary>
  97.  
    /// 函数入口
  98.  
    /// </summary>
  99.  
    /// <param name="args"></param>
  100.  
    static void Main(string[] args)
  101.  
    {
  102.  
    Console.WriteLine("InnerTask is runing");
  103.  
    InnerTask();
  104.  
    Console.WriteLine("InnerTaskAttachedToParent is runing");
  105.  
    InnerTaskAttachedToParent();
  106.  
    Console.WriteLine("MultiInnerTask is runing");
  107.  
    MultiInnerTask();
  108.  
     
  109.  
    Console.Read();
  110.  
    }
  111.  
    }
  112.  
    }

参考地址:http://www.cnblogs.com/yunfeifei/p/4111112.html

多线程带来的问题

1、死锁问题

  前面我们学习了Task的使用方法,其中Task的等待机制让我们瞬间爱上了它,但是如果我们在调用Task.WaitAll方法等待所有线程时,如果有一个Task一直不返回,会出现什么情况呢?当然,如果我们不退出来的话,程序会一直等待下去,那么因为这一个Task的死锁,导致其他的任务也无法正常提交,整个程序"死"在那里。下面我们来写一段代码,来看一下死锁的情况:

         var t1 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task 1 Start running...");
while(true)
{
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine("Task 1 Finished!");
});
var t2 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task 2 Start running...");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Task 2 Finished!");
});
Task.WaitAll(t1,t2);

这里我们创建两个Task,t1和t2,t1里面有个while循环,由于条件一直为TRUE,所以他永远也无法退出。运行程序,结果如下:

可以看到Task2完成了,就是迟迟等不到Task1,这个时候我们按回车是没有反应的,除非关掉窗口。如果我们在项目中遇到这种情况是令人很纠结的,因为我们也不知道到底发生了什么,程序就是停在那里,也不报错,也不继续执行。

那么出现这种情况我们该怎么处理呢?我们可以设置最大等待时间,如果超过了等待时间,就不再等待,下面我们来修改代码,设置最大等待时间为5秒(项目中可以根据实际情况设置),如果超过5秒就输出哪个任务出错了,代码如下:

           Task[] tasks = new Task[2];
tasks[0] = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task 1 Start running...");
while(true)
{
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine("Task 1 Finished!");
});
tasks[1] = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task 2 Start running...");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Task 2 Finished!");
}); Task.WaitAll(tasks,5000);
for (int i = 0; i < tasks.Length;i++ )
{
if (tasks[i].Status != TaskStatus.RanToCompletion)
{
Console.WriteLine("Task {0} Error!",i + 1);
}
}
Console.Read();

这里我们将所有任务放到一个数组里面进行管理,调用Task.WaitAll的一个重载方法,第一个参数是Task[]数据,第二个参数是最大等待时间,单位是毫秒,这里我们设置为5000及等待5秒钟,就继续向下执行。下面我们遍历Task数组,通过Status属性判断哪些Task没有完成,然后输出错误信息。

2、SpinLock(自旋锁)

   我们初识多线程或者多任务时,第一个想到的同步方法就是使用lock或者Monitor,然而在4.0 之后微软给我们提供了另一把利器——spinLock,它比重量级别的Monitor具有更小的性能开销,它的用法跟Monitor很相似,VS给的提示如下:

下面我们来写一个例子看一下,代码如下(关于lock和Monitor的用法就不再细说了,网上资料很多,大家可以看看):

          SpinLock slock = new SpinLock(false);
long sum1 = 0;
long sum2 = 0;
Parallel.For(0, 100000, i =>
{
sum1 += i;
}); Parallel.For(0, 100000, i =>
{
bool lockTaken = false;
try
{
slock.Enter(ref lockTaken);
sum2 += i;
}
finally
{
if (lockTaken)
slock.Exit(false);
}
}); Console.WriteLine("Num1的值为:{0}", sum1);
Console.WriteLine("Num2的值为:{0}", sum2); Console.Read();

输出结果如图:

这里我们使用了Parallel.For方法来做演示,Parallel.For用起来方便,但是在实际开发中还是尽量少用,因为它的不可控性太高,有点简单粗暴的感觉,可能带来一些不必要的"麻烦",最好还是使用Task,因为Task的可控性较好。

slock.Enter方法,解释如下:

3、多线程之间的数据同步

  多线程间的同步,在用thread的时候,我们常用的有lock和Monitor,上面刚刚介绍了.Net4.0中一个新的锁——SpinLock(自旋锁),实际上,我们还可以将任务分成多块,由多个线程一起执行,最后合并多个线程的结果,如:求1到100的和,我们分10个线程,分别求1~10,......,90~100的和,然后合并十个线程的结果。还有就是使用线程安全集合,可参加第二天的文章。其实Task的同步机制做已经很好了,如果有特殊业务需求,有线程同步问题,大家可一起交流~~

Task和线程池之间的抉择

  我们要说的task的知识也说的差不多了,接下来我们开始站在理论上了解下“线程池”和“任务”之间的关系,我们要做到知其然,还要知其所以然。不管是说线程还是任务,我们都不可避免的要讨论下线程池,然而在.net 4.0以后,线程池引擎考虑了未来的扩展性,已经充分利用多核微处理器架构,只要在可能的情况下,我们应该尽量使用task,而不是线程池。

  这里简要的分析下CLR线程池,其实线程池中有一个叫做“全局队列”的概念,每一次我们使用QueueUserWorkItem的使用都会产生一个“工作项”,然后“工作项”进入“全局队列”进行排队,最后线程池中的的工作线程以FIFO(First Input First Output)的形式取出,这里值得一提的是在.net 4.0之后“全局队列”采用了无锁算法,相比以前版本锁定“全局队列”带来的性能瓶颈有了很大的改观。那么任务委托的线程池不光有“全局队列”,而且每一个工作线程都有”局部队列“。我们的第一反应肯定就是“局部队列“有什么好处呢?这里暂且不说,我们先来看一下线程池中的任务分配,如下图:

线程池的工作方式大致如下,线程池的最小线程数是6,线程1~3正在执行任务1~3,当有新的任务时,就会向线程池请求新的线程,线程池会将空闲线程分配出去,当线程不足时,线程池就会创建新的线程来执行任务,直到线程池达到最大线程数(线程池满)。总的来说,只有有任务就会分配一个线程去执行,当FIFO十分频繁时,会造成很大的线程管理开销。

  下面我们来看一下task中是怎么做的,当我们new一个task的时候“工作项”就会进去”全局队列”,如果我们的task执行的非常快,那么“全局队列“就会FIFO的非常频繁,那么有什么办法缓解呢?当我们的task在嵌套的场景下,“局部队列”就要产生效果了,比如我们一个task里面有3个task,那么这3个task就会存在于“局部队列”中,如下图的任务一,里面有三个任务要执行,也就是产生了所谓的"局部队列",当任务三的线程执行完成时,就会从任务一种的队列中以FIFO的形式"窃取"任务执行,从而减少了线程管理的开销。这就相当于,有两个人,一个人干完了分配给自己的所有活,而另一个人却还有很多的活,闲的人应该接手点忙的人的活,一起快速完成。

  从上面种种情况我们看到,这些分流和负载都是普通ThreadPool.QueueUserWorkItem所不能办到的,所以说在.net 4.0之后,我们尽可能的使用TPL,抛弃ThreadPool。

原文地址:http://www.cnblogs.com/yunfeifei/p/4122084.html

Task底层实现原理探秘的更多相关文章

  1. 【Vue源码学习】响应式原理探秘

    最近准备开启Vue的源码学习,并且每一个Vue的重要知识点都会记录下来.我们知道Vue的核心理念是数据驱动视图,所有操作都只需要在数据层做处理,不必关心视图层的操作.这里先来学习Vue的响应式原理,V ...

  2. 老生常谈系列之Aop--CGLIB动态代理的底层实现原理

    老生常谈系列之Aop--CGLIB动态代理的底层实现原理 前言 上一篇老生常谈系列之Aop--JDK动态代理的底层实现原理简单讲解了JDK动态代理的实现,动态代理常用实现里面的双子星还有另一位--CG ...

  3. 老生常谈系列之Aop--JDK动态代理的底层实现原理

    老生常谈系列之Aop--JDK动态代理的底层实现原理 前言 在Aop系列里面有两篇文章,分别是老生常谈系列之Aop--Spring Aop原理浅析和老生常谈系列之Aop--Spring Aop源码解析 ...

  4. PHP底层工作原理

    最近搭建服务器,突然感觉lamp之间到底是怎么工作的,或者是怎么联系起来?平时只是写程序,重来没有思考过他们之间的工作原理: PHP底层工作原理 图1 php结构 从图上可以看出,php从下到上是一个 ...

  5. Java并发之底层实现原理学习笔记

    本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为 ...

  6. spirng底层实现原理

    什么是框架?框架解决的是什么问题? 编程有一个准则,Don't Repeat Yourself(不要重复你的代码),所以我们会将重复的代码抽取出来,封装到方法中:如果封装的方法过多,将将这些方法封装成 ...

  7. iOS weak底层实现原理

    今年年底做了很多决定,离开工作三年的深圳,来到了上海,发现深圳和上海在苹果这方面还是差距有点大的,上海的市场8成使用swift编程,而深圳8成的使用OC,这点还是比较让准备来上海打拼的苹果工程师有点小 ...

  8. Spring MVC 原理探秘 - 容器的创建过程

    1.简介 在上一篇文章中,我向大家介绍了 Spring MVC 是如何处理 HTTP 请求的.Spring MVC 可对外提供服务时,说明其已经处于了就绪状态.再次之前,Spring MVC 需要进行 ...

  9. 《Java并发编程的艺术》Java并发机制的底层实现原理(二)

    Java并发机制的底层实现原理 1.volatile volatile相当于轻量级的synchronized,在并发编程中保证数据的可见性,使用 valotile 修饰的变量,其内存模型会增加一个 L ...

随机推荐

  1. 部署kubernetes-prometheus和用kubespray部署kubernetes后修改kubelet的

    1.kubespray的配置文件 /opt/kubespray/inventory/mycluster/group_vars/all/all.yml# kube_read_only_port: 102 ...

  2. C语言Ⅰ博客作业10

    这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-3/homework/10097 我在这个课程的 ...

  3. Spring+SpringMvc+Hibernate整合记录

    Spring+SpringMvc+Hibernate+Maven整合各大配置文件记录 1.Idea新建的Maven架构 2.Maven的对象模型的内容 <project xmlns=" ...

  4. spring-boot 使用jdk6(三)

    环境 jdk 6 tomcat 7.0.59 sts 4.4.2 maven 3.2.5 背景 由于环境限制,还在使用 JDK6,所以需要将 spring boot 进行配置,支持JDK6. 以下所有 ...

  5. linux系统redis安装及使用

    1.下载redishttp://download.redis.io/releases/redis-5.0.5.tar.gz$ wget http://download.redis.io/release ...

  6. ThinkPHP composer的安装,及image图像处理类库的加载

    以下教程针对windows系统,示例系统使用win7 composer安装 下载composer安装包,点击安装. 出现'composer choose the command-line php' 要 ...

  7. python设计购物车

    设计购物车 一需求: 1.启动程序后,输入用户名密码后,如果是第一次登录,让用户输入工资,然后打印商品列表 2.允许用户根据商品编号购买商品 3.用户选择商品后,检测余额是否够,够就直接扣款,不够就提 ...

  8. jenkins转换显示语言为中文简体(jenkins汉化)

    jenkins版本2.117 单位使用的jenkins一直是英文版本,有同事建议切换为中文版. 以下过程完成转换. 一.安装插件 主界面-->系统管理-->插件管理-->可选插件 图 ...

  9. 排好序的数组中,找出两数之和为m的所有组合

    public static void main(String[] args) { int[] a = {1,2,2,3,3,4,5,6}; int m = 6; normal(a, m); } //正 ...

  10. HTML5之fileReader异步读取文件及文件切片读取

    fileReader的方法与事件 fileReade实现图片预加载 fileReade实现文件读取进度条 fileReade的与file.s实现文件切片读取 一.fileReader的方法与事件 1. ...