对于多线程,我们经常使用的是Thread。在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别呢?

任务和线程的区别:

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

2、任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。

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

1、认识Task

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

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

2、创建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:任务执行完毕。

二、Task的任务控制

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

1、Task.Wait

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

2、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!

3、Task.WaitAny

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

4、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

5、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();

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

好了,今天先说道这里,明天继续讲task,接下来该说说task的异常处理和其他的一些用法,如果喜欢可以关注我,一有更新会马上通知你。

作者:雲霏霏

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

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

认识和使用Task的更多相关文章

  1. Concepts:Request 和 Task

    当SQL Server Engine 接收到Session发出的Request时,SQL Server OS将Request和Task绑定,并为Task分配一个Workder.在TSQL Query执 ...

  2. .Net多线程编程—任务Task

    1 System.Threading.Tasks.Task简介 一个Task表示一个异步操作,Task的创建和执行是独立的. 只读属性: 返回值 名称 说明 object AsyncState 表示在 ...

  3. nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)

    本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,上一篇分享文章制作是在windows上使用的nginx,一般正式发布的时候是在linux来配 ...

  4. windows+nginx+iis+redis+Task.MainForm构建分布式架构 之 (nginx+iis构建服务集群)

    本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,由标题就能看出此内容不是一篇分享文章能说完的,所以我打算分几篇分享文章来讲解,一步一步实现分 ...

  5. Openstack Periodic Task

    Openstack Periodic Task 周期性任务在各个模块的manager.py(computer,scheduler,cell,network)中添加. 添加方法:在模块manager类实 ...

  6. MapReduce剖析笔记之三:Job的Map/Reduce Task初始化

    上一节分析了Job由JobClient提交到JobTracker的流程,利用RPC机制,JobTracker接收到Job ID和Job所在HDFS的目录,够早了JobInProgress对象,丢入队列 ...

  7. [Java定时器]用Spring Task实现一个简单的定时器.

    今天做一个项目的的时候需要用到定时器功能.具体需求是: 每个月一号触发一次某个类中的方法去拉取别人的接口获取上一个月份车险过期的用户.如若转载请附上原文链接:http://www.cnblogs.co ...

  8. 定时管理器框架-Task.MainForm

    入住博客园4年多了,一直都是看别人的博客,学习别人的知识,为各个默默无私贡献自己技术总结的朋友们顶一个:这几天突然觉得是时候加入该队列中,贡献出自己微弱的力量,努力做到每个月有不同学习总结,知识学习的 ...

  9. Task三个列子的分享

    这次要分享的是C#Task任务的几个列子,感觉最实用的是封装的分页任务执行方法,这个方法步奏也是目前在我工作中执行多任务常用的,不知道各位也有这用的情况,那么开始吧. 1.顺序任务执行 //顺序任务执 ...

  10. webapi+Task并行请求不同接口实例

    标题的名称定义不知道是否准确,不过我想表达的意思就是使用Task特性来同时请求多个不同的接口,然后合并数据:我想这种场景的开发对于对接过其他公司接口的人不会陌生,本人也是列属于之内,更多的是使用最原始 ...

随机推荐

  1. NotesDocument 作为函数返回值的问题

    如下代码 Set recipientDoc = getDocInNAB(“search string”) If Not recipientDoc Is Nothing Then ‘In debug I ...

  2. 【转】Jenkins怎么启动和停止服务

    笔者没有把Jenkins配置到tomcat中,每次都是用命令行来启动Jenkins.但是遇到一个问题:Jenkins一直是开着的,想关闭也关闭不了.百度了一些资料,均不靠谱(必须吐槽一下百度).于是进 ...

  3. 给X240换上了三键触摸板

    X240自带的触摸板非常不好用, 对于我这样的指点杆重度用户, 每次要按下整块板真的是太费力了, 而且在夜里声音很吵. 在淘宝上买了三键的X250的触摸板换上了. 这是购买的触摸板的型号 换的时候, ...

  4. ios中NSObject分类

    #import <Foundation/Foundation.h> #define BUNDLE_PATH_IMAGENAME(c) [[NSBundle mainBundle] path ...

  5. iOS 中的各种锁

    在日常开发过程中,为了提升程序运行效率,以及用户体验,我们经常使用多线程.在使用多线程的过程中,难免会遇到资源竞争问题.我们采用锁的机制来确保线程安全. 线程安全 当一个线程访问数据的时候,其他的线程 ...

  6. kafka负载均衡相关资料收集(一)

    key为null时Kafka会将消息发送给哪个分区? 当你编写kafka Producer时, 会生成KeyedMessage对象. 1 KeyedMessage<K, V> keyedM ...

  7. memcached全面剖析--5. memcached的应用和兼容程序

    我是Mixi的长野.memcached的连载终于要结束了.到上次为止,我们介绍了与memcached直接相关的话题,本次介绍一些mixi的案例和实际应用上的话题,并介绍一些与memcached兼容的程 ...

  8. 关于Apache (httpd)服务器防DDOS模块mod_evasive的使用说明

    关于Apache (httpd)服务器防DDOS模块mod_evasive的使用说明 1. mod_evasive 介绍: mod_evasive 是Apache(httpd)服务器的防DDOS的一个 ...

  9. TextSharp详情

    TextSharp是一个生成Pdf文件的开源项目,最近在项目中有使用到这个项目,对使用中的经验作一个小结. ITextSharp中相关的概念: 一.Document 这个对象有三个构造函数: 隐藏行号 ...

  10. 【JavaScript】浅析JavaScript对象如何添加属性和方法

    向JavaScript类中添加属性和方法,最直观的做法就是在类中定义属性和方法.JavaScript是一门弱语言,除了直接定义还可以用prototype来添加. 下面介绍从外部向JavaScript添 ...