对于多线程,我们经常使用的是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();
         Console.ReadKey();//这句忘了加,程序退出了,看不到“Abort mission success!“这个提示

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

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

异步编程能充分利用多核计算机,提升程序性能。

请看如下程序:

static object o = new object();
static void Main(string[] args)
{ var tf = new TaskFactory();//线程池对象
Task t1 = tf.StartNew(TaskMenthod, "");//创建并启用线程方法1
Task t2 = Task.Factory.StartNew(TaskMenthod, "");//创建并启用线程方法2
Task t3 = new System.Threading.Tasks.Task(TaskMenthod, "");
t3.Start();//创建并启用线程方法3
Task t4 = Task.Run(() => TaskMenthod(""));//创建并启用线程方法4 //
Task t5 = Task.Run(() => TaskMenthod(""));
Task t6 = Task.Run(() => TaskMenthod(""));
Task t7 = Task.Run(() => TaskMenthod(""));
Task t8 = Task.Run(() => TaskMenthod(""));
Task t9 = Task.Run(() => TaskMenthod(""));
Task t10 = Task.Run(() => TaskMenthod(""));
Console.ReadKey();
} static void TaskMenthod(object title)
{
lock (o)//确保一个线程执行完方法后 其他线程才能进入
{
//连续输出四次
Console.WriteLine(title.ToString());
Console.WriteLine(title.ToString());
Console.WriteLine(title.ToString());
Console.WriteLine(title.ToString());
}
}

运行结果

因为Task是异步的,所以输出的结果不是按照1~10的顺序输出的,相信这点大家都能理解。

但是:

我们发现本应在一个方法体内连续输出的四个9却被分成了两部分,为何会这样呢?

这是因为多个Task同时访问TaskMenthod方法,他们之间是无序的,各个Task之间是没有优先级别的,因此会造成上述问题。

如何解决呢?

通过C# LOCK 可以解决,我们对TaskMenthod方法进行加锁即可,代码修正如下:

2、Task的Wait方法

运行代码如下:

  class Program
{
static object o = new object();
static void Main(string[] args)
{
var tf = new TaskFactory();//
Task t1 = tf.StartNew(TaskMenthod, "");
t1.Wait();//等待线程1 执行完毕后 继续执行
Task t2 = Task.Factory.StartNew(TaskMenthod, "");
t2.Wait();
Task t3 = new System.Threading.Tasks.Task(() => TaskMenthod(""));
t3.Start();
t3.Wait();
Task t4 = Task.Run(() => TaskMenthod(""));
t4.Wait(); //
Task t5 = Task.Run(() => TaskMenthod(""));
Task t6 = Task.Run(() => TaskMenthod(""));
Task t7 = Task.Run(() => TaskMenthod(""));
Task t8 = Task.Run(() => TaskMenthod(""));
Task t9 = Task.Run(() => TaskMenthod(""));
Task t10 = Task.Run(() => TaskMenthod(""));
Console.ReadKey();
} static void TaskMenthod(object title)
{
lock (o)//确保一个线程执行完方法后 其他线程才能进入
{
Console.WriteLine(title);
}
}
}

以上程序中t1 t2 t3 t4 这四个Task进行了Wait操作,也就是说:其他程序必须等待线程 1 2 3 4 执行完毕后,才能执行,而且线程 1 2 3 4 完全按照代码编写顺序进行输出。

三次执行结果如下:

由上图可知,线程1 2 3 4 的输出牢牢的把控了前四的位置,且是严格按照顺序输出的。

3、Task的WaitAll方法

将上述代码修改为如下:

class Program
{
static object o = new object();
static void Main(string[] args)
{
var tf = new TaskFactory();//线程池对象
Task t1 = tf.StartNew(TaskMenthod, "");
Task t2 = Task.Factory.StartNew(TaskMenthod, "");
Task t3 = new System.Threading.Tasks.Task(() => TaskMenthod(""));
t3.Start();
Task t4 = Task.Run(() => TaskMenthod(""));
Task.WaitAll(t1, t2, t3, t4); //优先执行线程1 2 3 4 线程 1 2 3 4 之间是异步的,也就是说执行顺序是无序的 待线程 1 2 3 4 执行完毕后 才会执行后续的线程 //
Task t5 = Task.Run(() => TaskMenthod(""));
Task t6 = Task.Run(() => TaskMenthod(""));
Task t7 = Task.Run(() => TaskMenthod(""));
Task t8 = Task.Run(() => TaskMenthod(""));
Task t9 = Task.Run(() => TaskMenthod(""));
Task t10 = Task.Run(() => TaskMenthod(""));
Console.ReadKey();
} static void TaskMenthod(object title)
{
lock (o)//确保一个线程执行完方法后 其他线程才能进入
{
Console.WriteLine(title);
}
}
}

上面有句注释:Task.WaitAll(t1, t2, t3, t4); //优先执行线程1 2 3 4 线程 1 2 3 4 之间是异步的,也就是说执行顺序是无序的 待线程 1 2 3 4 执行完毕后 才会执行后续的线程

三次执行结果:

由图可知,t1 t2 t3 t4 这四个线程优先执行,但是这四个线程之间的执行是异步的。理解这一点很重要。

继续探讨WaitAll方法

由代码 Task.WaitAll(t1, t2, t3, t4); 可知,必须等待 t1 t2 t3 t4 这四个线程执行完毕后,才会执行下面的操作。、

在此假设名字为t3的线程,我们将 t3.Start(); 去掉,看看会是社么结果:

代码如下:

    class Program
{
static object o = new object();
static void Main(string[] args)
{
var tf = new TaskFactory();//线程池对象
Task t1 = tf.StartNew(TaskMenthod, "");
Task t2 = Task.Factory.StartNew(TaskMenthod, "");
Task t3 = new System.Threading.Tasks.Task(() => TaskMenthod(""));
//t3.Start(); //t3 不执行了
Task t4 = Task.Run(() => TaskMenthod(""));
Task.WaitAll(t1, t2, t3, t4); //优先执行线程1 2 3 4 线程 1 2 3 4 之间是异步的,也就是说执行顺序是无序的 待线程 1 2 3 4 执行完毕后 才会执行后续的线程 //
Task t5 = Task.Run(() => TaskMenthod(""));
Task t6 = Task.Run(() => TaskMenthod(""));
Task t7 = Task.Run(() => TaskMenthod(""));
Task t8 = Task.Run(() => TaskMenthod(""));
Task t9 = Task.Run(() => TaskMenthod(""));
Task t10 = Task.Run(() => TaskMenthod(""));
Console.ReadKey();
} static void TaskMenthod(object title)
{
lock (o)//确保一个线程执行完方法后 其他线程才能进入
{
Console.WriteLine(title);
}
}
}

执行结果:

由此可知:因为 t3 的罢工,导致其他线程一直处于等待状态(得不到执行),如何解决这个问题呢?

还好,WaitAll方法提供的重载方法中有一个和时间结合的,如下:

将代码修改如下:

    class Program
{
static object o = new object();
static void Main(string[] args)
{
var tf = new TaskFactory();//线程池对象
Task t1 = tf.StartNew(TaskMenthod, "");
Task t2 = Task.Factory.StartNew(TaskMenthod, "");
Task t3 = new System.Threading.Tasks.Task(() => TaskMenthod(""));
//t3.Start(); //t3 不执行了 罢工了 咋的吧
Task t4 = Task.Run(() => TaskMenthod("")); Task[] tskArray = new Task[] { t1, t2, t3, t4 };
Task.WaitAll(tskArray,); //等待三秒后,执行后续线程 //
Task t5 = Task.Run(() => TaskMenthod(""));
Task t6 = Task.Run(() => TaskMenthod(""));
Task t7 = Task.Run(() => TaskMenthod(""));
Task t8 = Task.Run(() => TaskMenthod(""));
Task t9 = Task.Run(() => TaskMenthod(""));
Task t10 = Task.Run(() => TaskMenthod(""));
Console.ReadKey();
} static void TaskMenthod(object title)
{
lock (o)//确保一个线程执行完方法后 其他线程才能进入
{
Console.WriteLine(title);
}
}
}

这样,在程序等待三秒后,会执行后续操作!

4、WaitAny 方法,既是等待任意线程执行完毕后,执行后续方法

将上述程序中的WaitAll 修改成WaitAny 试一下

    class Program
{
static object o = new object();
static void Main(string[] args)
{
var tf = new TaskFactory();//线程池对象
Task t1 = tf.StartNew(TaskMenthod, "");
Task t2 = Task.Factory.StartNew(TaskMenthod, "");
Task t3 = new System.Threading.Tasks.Task(() => TaskMenthod(""));
//t3.Start(); //t3 不执行了 罢工了 咋的吧
Task t4 = Task.Run(() => TaskMenthod("")); Task.WaitAny(t1, t2, t3, t4); // //
Task t5 = Task.Run(() => TaskMenthod(""));
Task t6 = Task.Run(() => TaskMenthod(""));
Task t7 = Task.Run(() => TaskMenthod(""));
Task t8 = Task.Run(() => TaskMenthod(""));
Task t9 = Task.Run(() => TaskMenthod(""));
Task t10 = Task.Run(() => TaskMenthod(""));
Console.ReadKey();
} static void TaskMenthod(object title)
{
lock (o)//确保一个线程执行完方法后 其他线程才能进入
{
Console.WriteLine(title);
}
}
}

本节简单的介绍了Task的用法,希望大家喜欢!

@陈卧龙的博客

Task 异步编程测试案例及基础应用说明的更多相关文章

  1. Task异步编程

    Task异步编程中,可以实现在等待耗时任务的同时,执行不依赖于该耗时任务结果的其他同步任务,提高效率. 1.Task异步编程方法签名及返回值: a) 签名有async 修饰符 b) 方法名以 Asyn ...

  2. c#中的Task异步编程

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/index翻译 1. 引入 Task异步 ...

  3. 新手浅谈C#Task异步编程

    Task是微软在.net framework 4.0发布的新的异步编程的利器,当然4.5新增了async.await,这儿我们先说Task相关. 在实际编程中,我们用的较多的是Task.Task.Fa ...

  4. 新手浅谈Task异步编程和Thread多线程编程

    初学Task的时候上网搜索,看到很多文章的标题都是task取代thread等等相关,我也一直以为task和thread是一类,其实task是.net4.0提出的异步编程,在之前.net1.0有dele ...

  5. vertx异步编程测试

    vertx是异步编程的框架,性能较高,开发简单.异步编程就是当一个请求来了,vertx将其交由一个事件进行处理,然后继续向下执行,等处理完成,返回结果,通知客户端.这是一个由服务端反向调用客户端的过程 ...

  6. Task异步编程,刨根到底

    1. 编译器到底对await做了什么 await 一个异步操作的时候,实际上编译器会创建一个状态机,这个状态机包含了调用者的上下文变量,状态机使用yield迭代器实现,状态机由clr调度,每次运行都会 ...

  7. .NET 4.5 Task异步编程学习资料

    参考资料: 1. http://www.cnblogs.com/heyuquan/archive/2013/04/18/3028044.html

  8. 『审慎』.Net4.6 Task 异步函数 比 同步函数 慢5倍 踩坑经历

    异步Task简单介绍 本标题有点 哗众取宠,各位都别介意(不排除个人技术能力问题) —— 接下来:我将会用一个小Demo 把 本文思想阐述清楚. .Net 4.0 就有了 Task 函数 —— 异步编 ...

  9. Net4.6 Task 异步函数 比 同步函数 慢5倍 踩坑经历

    Net4.6 Task 异步函数 比 同步函数 慢5倍 踩坑经历 https://www.cnblogs.com/shuxiaolong/p/DotNet_Task_BUG.html 异步Task简单 ...

随机推荐

  1. IE和Chrome执行javascript对鼠标双击事件的不同响应

    最近在用天地图API帮同学做点开发的工作,主要是基于天地图的API实现违法用地举报的在线地图标绘,要实现的效果如下: 由于是基于天地图API的TPolygonTool工具实现面积量测和多边形绘制功能, ...

  2. 安全测试 web应用安全测试之XXS跨站脚本攻击检测

    web应用安全测试之XXS跨站脚本攻击检测 by:授客 QQ:1033553122 说明 意在对XSS跨站脚本攻击做的简单介绍,让大家对xss攻击有个初步认识,并能够在实际工作当中运用本文所述知识做些 ...

  3. Python笔记(十四):操作excel openpyxl模块

    (一)  常遇到的情况 就我自己来说,常遇到的情况可能就下面几种: 读取excel整个sheet页的数据. 读取指定行.列的数据 往一个空白的excel文档写数据 往一个已经有数据的excel文档追加 ...

  4. 使用sklearn机器学习库实现线性回归

    import numpy as np  # 导入科学技术框架import matplotlib.pyplot as plt  # 导入画图工具from sklearn.linear_model imp ...

  5. Javascript 高级程序设计--总结【二】

    **********************  Chapter 6  ********************** 属性: 数据属性: Configurable: 能否通过delete 删除属性,默认 ...

  6. BootStrap DateTimePicker的使用

    使用方法 1. 添加资源 2. 编写代码 <!DOCTYPE html> <html> <head> <meta http-equiv="Conte ...

  7. Python学习—Pycharm连接mysql服务器

    安装pymysql pip3 install pymysql 安装Mysql客户端驱动(基于Pycharm工具) 点击download,下载mysql驱动 等待驱动安装成功后,点击OK即可 创建数据库 ...

  8. JavaScript 中的匿名函数((function() {})();)与变量的作用域

    以前都是直接用前端框架Bootstrap,突然想看看Javascript,发现javascript是个非常有趣的东西,这里把刚碰到的一个小问题的理解做下笔录(废话不多说,上代码). /** * Exa ...

  9. SSRS奇怪报错Could not update a list of fields for the quer.

    今天遇到一个奇怪的问题,SSRS我觉得是个半成品,很多东西都搞不了.写了一段SQL,本来SQL写法都有点怪了,如下 WITH TMP_A AS (SELECT *,ROW_NUMBER() OVER( ...

  10. 真机测试以及appstore发布流程

    一.添加真机测试流程:http://jingyan.baidu.com/article/48b558e33b96a27f38c09aa4.html 二.app发布流程:http://jingyan.b ...