.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. SpringCloud学习(八)消息总线(Spring Cloud Bus)(Finchley版本)

    Spring Cloud Bus 将分布式的节点用轻量的消息代理连接起来.它可以用于广播配置文件的更改或者服务之间的通讯,也可以用于监控.本文要讲述的是用Spring Cloud Bus实现通知微服务 ...

  2. 一篇文章带你了解SQL注入

    什么是SQL注入? 原理: Web应用程序对用户输入的数据校验处理不严或者根本没有校验,致使用户可以拼接执行SQL命令 危害: 注入可能导致数据丢失泄露或数据破坏.缺乏可审计性,有时甚至能导致完全接管 ...

  3. HDU 1688 Sightseeing 【输出最短路+次短路条数】

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1688 题目大意:给n个点,m条有向边.再给出起点s, 终点t.求出s到t的最短路条数+次短路条数. 思 ...

  4. 12.Flume的安装

    先把flume包上传并解压 给flume创建一个软链接 给flume配置环境变量 #flume export FLUME_HOME=/opt/modules/flume export PATH=$PA ...

  5. PTA(Advanced Level)1075.PAT Judge

    The ranklist of PAT is generated from the status list, which shows the scores of the submissions. Th ...

  6. PTA (Advanced Level)1077.Kuchiguse

    The Japanese language is notorious for its sentence ending particles. Personal preference of such pa ...

  7. [转帖]k8s国内镜像

    k8s国内镜像 https://www.jianshu.com/p/b9fecdb5e3a7 wu_sphinx 关注 2019.05.06 20:43* 字数 155 阅读 628评论 0喜欢 0 ...

  8. Redis 是怎么实现 “附近的人” 的?

    针对"附近的人"这一位置服务领域的应用场景,常见的可使用PG.MySQL和MongoDB等多种DB的空间索引进行实现. 而Redis另辟蹊径,结合其有序队列zset以及geohas ...

  9. ajax同源策略,jsonP跨域访问

    浏览器处于安全性的考虑,要求ajax请求,必须满足同源策略 规定:访问的协议://域名:端口号都相同时满足同源策略,浏览器可以正确解析数据,否则如果有一项不满足要求,则属于跨域访问,浏览器可以正常获取 ...

  10. Linux就该这么学——初识管道符

    初识管道命令符 管道命令符本质(就是一个“任意门”) 把前一个命令原本要输出到屏幕的标准正常数据当做是最后一个命令的标准输入 格式 : “命令A | 命令B | ...” 示例 : 1.找出被限制登录 ...