5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task

5天玩转C#并行和多线程编程系列文章目录html

5天玩转C#并行和多线程编程 —— 第一天 认识Parallel编程

5天玩转C#并行和多线程编程 —— 次日 并行集合和PLinq多线程

5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task架构

5天玩转C#并行和多线程编程 —— 第四天 Task进阶异步

5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结函数

 

  对于多线程,咱们常用的是Thread。在咱们了解Task以前,若是咱们要使用多核的功能可能就会本身来开线程,然而这种线程模型在.net 4.0以后被一种称为基于“任务的编程模型”所冲击,由于task会比thread具备更小的性能开销,不过你们确定会有疑惑,任务和线程到底有什么区别呢?post

 任务和线程的区别:性能

一、任务是架构在线程之上的,也就是说任务最终仍是要抛给线程去执行。spa

二、任务跟线程不是一对一的关系,好比开10个任务并非说会开10个线程,这一点任务有点相似线程池,可是任务相比线程池有很小的开销和精确的控制。.net

 

 1、认识Task和Task的基本使用

一、认识Task

首先来看一下Task的继承结构。Task标识一个异步操做。

能够看到Task和Thread同样,位于System.Threading命名空间下,这也就是说他们直接有密不可分的联系。下面咱们来仔细看一下吧!

 

二、建立Task

建立Task的方法有两种,一种是直接建立——new一个出来,一种是经过工厂建立。下面来看一下这两种建立方法:

        //第一种建立方式,直接实例化
         var task1 = new Task(() =>
         {
            //TODO you code
         });

这是最简单的建立方法,能够看到其构造函数是一个Action,其构造函数有以下几种,比较经常使用的是前两种。

        //第二种建立方式,工厂建立
         var task2 = Task.Factory.StartNew(() =>
         {
            //TODO you code
         });

这种方式经过静态工厂,建立以个Task并运行。下面咱们来建一个控制台项目,演示一下,代码以下:

要添加System.Threading.Tasks命名控件引用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TaskDemo
{
   class Program
   {
      static void Main(string[] args)
      {
         var task1 = new Task(() =>
         {
            Console.WriteLine("Hello,task");
         });
         task1.Start();

         var task2 = Task.Factory.StartNew(() =>
         {
            Console.WriteLine("Hello,task started by task factory");
         });

         Console.Read();
      }
   }
}

 这里我分别用两种方式建立两个task,并让他们运行。能够看到经过构造函数建立的task,必须手动Start,而经过工厂建立的Task直接就启动了。

下面咱们来看一下Task的声明周期,编写以下代码:

      var task1 = new Task(() =>
         {
            Console.WriteLine("Begin");
            System.Threading.Thread.Sleep(2000);
            Console.WriteLine("Finish");
         });
         Console.WriteLine("Before start:" + task1.Status);
         task1.Start();
         Console.WriteLine("After start:" + task1.Status);
         task1.Wait();
         Console.WriteLine("After Finish:" + task1.Status);

         Console.Read();

  task1.Status就是输出task的当前状态,其输出结果以下:

能够看到调用Start前的状态是Created,而后等待分配线程去执行,到最后执行完成。

从咱们能够得出Task的简略生命周期:

Created:表示默认初始化任务,可是“工厂建立的”实例直接跳过。

WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。

RanToCompletion:任务执行完毕。

 

 2、Task的任务控制

  Task最吸引人的地方就是他的任务控制了,你能够很好的控制task的执行顺序,让多个task有序的工做。下面来详细说一下:

一、Task.Wait

在上个例子中,咱们已经使用过了,task1.Wait();就是等待任务执行完成,咱们能够看到最后task1的状态变为Completed。

 

二、Task.WaitAll

看字面意思就知道,就是等待全部的任务都执行完成,下面咱们来写一段代码演示一下:

    static void Main(string[] args)
      {
         var task1 = new Task(() =>
         {
            Console.WriteLine("Task 1 Begin");
            System.Threading.Thread.Sleep(2000);
            Console.WriteLine("Task 1 Finish");
         });
         var task2 = new Task(() =>
         {
            Console.WriteLine("Task 2 Begin");
            System.Threading.Thread.Sleep(3000);
            Console.WriteLine("Task 2 Finish");
         });
         
         task1.Start();
         task2.Start();
         Task.WaitAll(task1, task2);
         Console.WriteLine("All task finished!");

         Console.Read();
      }

其输出结果以下:

能够看到,任务一和任务二都完成之后,才输出All task finished!

 

三、Task.WaitAny

这个用发同Task.WaitAll,就是等待任何一个任务完成就继续向下执行,将上面的代码WaitAll替换为WaitAny,输出结果以下:

 

四、Task.ContinueWith

就是在第一个Task完成后自动启动下一个Task,实现Task的延续,下面咱们来看下他的用法,编写以下代码:

static void Main(string[] args)
      {
         var task1 = new Task(() =>
         {
            Console.WriteLine("Task 1 Begin");
            System.Threading.Thread.Sleep(2000);
            Console.WriteLine("Task 1 Finish");
         });
         var task2 = new Task(() =>
         {
            Console.WriteLine("Task 2 Begin");
            System.Threading.Thread.Sleep(3000);
            Console.WriteLine("Task 2 Finish");
         });

         
         task1.Start();
         task2.Start();
         var result = task1.ContinueWith<string>(task =>
         {
            Console.WriteLine("task1 finished!");
            return "This is task result!";
         });

         Console.WriteLine(result.Result.ToString());


         Console.Read();
      }

执行结果以下:

能够看到,task1完成以后,开始执行后面的内容,而且这里咱们取得task的返回值。

在每次调用ContinueWith方法时,每次会把上次Task的引用传入进来,以便检测上次Task的状态,好比咱们可使用上次Task的Result属性来获取返回值。咱们还能够这么写:

var SendFeedBackTask = Task.Factory.StartNew(() => { Console.WriteLine("Get some Data!"); })
                            .ContinueWith<bool>(s => { return true; })
                            .ContinueWith<string>(r => 
                              {
                                 if (r.Result)
                                 {
                                    return "Finished";
                                 }
                                 else
                                 {
                                    return "Error";
                                 }
                              });
         Console.WriteLine(SendFeedBackTask.Result);

首先输出Get some data,而后执行第二个得到返回值true,最后根据判断返回Finished或error。输出结果:

Get some Data!

Finished

其实上面的写法简化一下,能够这样写:

Task.Factory.StartNew<string>(() => {return "One";}).ContinueWith(ss => { Console.WriteLine(ss.Result);});

输出One,这个能够看明白了吧~

 更多ContinueWith用法参见:http://technet.microsoft.com/zh-CN/library/dd321405

 

五、Task的取消

前面说了那么多Task的用法,下面来讲下Task的取消,好比咱们启动了一个task,出现异常或者用户点击取消等等,咱们能够取消这个任务。

如何取消一个Task呢,咱们经过cancellation的tokens来取消一个Task。在不少Task的Body里面包含循环,咱们能够在轮询的时候判断IsCancellationRequested属性是否为True,若是是True的话就return或者抛出异常,抛出异常后面再说,由于尚未说异常处理的东西。

下面在代码中看下如何实现任务的取消,代码以下:

var tokenSource = new CancellationTokenSource();
         var token = tokenSource.Token;
         var task = Task.Factory.StartNew(() =>
         {
            for (var i = 0; i < 1000; i++)
            {
               System.Threading.Thread.Sleep(1000);
               if (token.IsCancellationRequested)
               {
                  Console.WriteLine("Abort mission success!");
                  return;
               }
            }
         }, token);
         token.Register(() =>
         {
            Console.WriteLine("Canceled");
         });
         Console.WriteLine("Press enter to cancel task...");
         Console.ReadKey();
         tokenSource.Cancel();
         Console.ReadKey();//这句忘了加,程序退出了,看不到“Abort mission success!“这个提示
 

这里开启了一个Task,并给token注册了一个方法,输出一条信息,而后执行ReadKey开始等待用户输入,用户点击回车后,执行tokenSource.Cancel方法,取消任务。其输出结果以下:

 

好了,今天先说道这里,明天继续讲task,接下来该说说task的异常处理和其余的一些用法,若是喜欢能够关注我,一有更新会立刻通知你。

 

 做者:雲霏霏

 博客地址:http://www.cnblogs.com/yunfeifei/

 声明:本博客原创文字只表明本人工做中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未受权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文链接。

相关文章
相关标签/搜索