Task是可能有延迟的工作单元,目的是生成一个结果值,或产生想要的效果。任务和线程的区别是:任务代表需要执行的作业,而线程代表做这个作业的工作者。

在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法。一般它使用的主要场景只是将一个工作任务丢给一个后台线程执行而已。为了高度可配置机制它提供了很多重载方法,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至控制任务的调度行为。所有这些能力也带来了复杂性的提升,你必须知道何时应该使用何种重载方法,提供哪种调度方式等等。

例如:

Task.Factory.StartNew(someAction, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

使用这些默认参数,Task.Run就能用于大多数情况——只是将任务简单的交给后台线程池去执行。当然你可以通过控制TaskCreationOptions参数来控制任务的行为,你也可以通过控制TaskScheduler来控制任务应该如何排队如何运行,你也可以使用重载方法中的接受对象状态那个参数,对于一些性能敏感的代码,它可以用于避免闭包以及相应的资源分配。

Task.Factory.StartNew这种写法不够简洁明快,于是,在.NET Framework 4.5及以后的版本中,微软引进了新的Task.Run方法。新方法不是为了替代旧的Task.Factory.StartNew方法,只是提供了一种使用Task.Factory.StartNew方法的更简洁的形式,而不需要去指定那一系列参数。这是一个捷径,事实上,Task.Run的内部实现逻辑跟Task.Factory.StartNew一样,只是传递了一些默认参数。

Task.Run(someAction)提供了以下八种重载方式,用于提供下面这几种组合情况:

public static Task Run(Func<Task> function);
/*
参数: function(以异步方式执行的工作量)、cancellationToken(应用以取消工作的取消标记)
返回结果:表示在线程池执行的队列的任务 (TResult)
*/
public static Task Run(Func<Task> function, CancellationToken cancellationToken);
public static Task Run(Action action);
/*
参数: action(以异步方式执行的工作量)、cancellationToken(应用以取消工作的取消标记)
返回结果:表示在线程池执行的队列的任务
*/
public static Task Run(Action action, CancellationToken cancellationToken);
public static Task<TResult> Run<TResult>(Func<TResult> function);
/*
参数: function(以异步方式执行的工作量)、cancellationToken(应用以取消工作的取消标记)
类型参数: TResult(任务的结果类型)
返回结果:表示在线程池执行的队列的任务 (TResult)
*/
public static Task<TResult> Run<TResult>(Func<TResult> function, CancellationToken cancellationToken); public static Task<TResult> Run<TResult>(Func<Task<TResult>> function);
/*
参数: function(以异步方式执行的工作量)、cancellationToken(应用以取消工作的取消标记)
类型参数: TResult(代理 Task 返回的结果的类型)
返回结果:表示由 function 返回的任务 (TResult) 的代理的任务 (TResult)
*/
public static Task<TResult> Run<TResult>(Func<Task<TResult>> function, CancellationToken cancellationToken);

实例解释:

var t = Task.Factory.StartNew(() =>
{
Task inner = Task.Factory.StartNew(() => { });
return inner;
});

这里t的类型会被推断为Task<Task>,因为此处任务的委托类型是Func<TResult>,所以这里TResult的类型就是Task,于是StartNew方法就返回Task<Task>,类似的,我可以改变成下面这种写法:

var t = Task.Factory.StartNew(() =>
{
Task<int> inner = Task.Factory.StartNew(() => );
return inner;
});

此处的t的类型自然是Task<Task<int>>,任务的委托类型还是Func<TResult>,TResult的类型就是Task<int>,StartNew方法就返回Task<Task<int>>。这有什么关系呢?考虑下如果我们现在使用下面这种写法:

var t = Task.Run(async delegate
{
await Task.Delay();
return ;
});

这里使用了async关键词,编译器会将这个委托映射成Func<Task<int>>,调用这个委托最终会返回Task<int>。因为这个这个委托是Func<Task<int>>,TResult的类型就是Task<int>,所以最后t的类型应该是Task<Task<int>>,而不是Task<int>。
在.Net 4中引入了Unwrap方法。Unwrap方法有两种重载形式,均是扩展方法的形式,一种是针对类型Task<Task>,另一种是针对<Task<TResult>>。微软只所以要把这个方法命名为解包(Unwrap),是因为这个方法可以返回任务的实际结果。对Task<Task>调用Unwrap方法可以返回一个新的Task(就像内部任务的一个代理一样)代表它的内部任务。相似的,对Task<Task<TResult>>调用Unwrap返回一个新的Task<TResult>代表它的内部任务。但是,如果外部任务失败了或者取消了,就不会有内部任务了,因为没有任务运行完成,所以代理任务也就变成了外部任务的状态。回到前面的例子,如果想让t代表内部任务的返回值(在这个例子中,这个值是42),那么应该像下面这样写:

            var t = Task.Factory.StartNew(async delegate
{
await Task.Delay();
return ;
}).Unwrap();

现在,变量t的类型是Task<int>,代表异步调用的结果。

因为微软想让开发者尽可能的使用Task.Run这种新的形式来启用后台任务,并且可以配合async/await使用,所以微软决定在Task.Run方法中内建unwrapping的功能。总的来说,Task.Run方法提供了上面Task.Factory.StartNew方法相同的unwrapping操作。于是,我们可以这样写:

         var t = Task.Run(async delegate
{
await Task.Delay();
return ;
});

t的类型是Task<int>,此处Task.Run执行的重载方法等价于:

            var t = Task.Factory.StartNew(async delegate
{
await Task.Delay();
return ;
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap();

上面讲的全部类容都意味着你可以使用Task.Run调用标准的lambdas/anonymous方法或是异步lambdas/anonymous方法,最后总会按你所期望的行为运行。如果我们想让任务在后台运行并且想等待它的结果,那么可以像下面这样写(例1):

public async Task GetResult()
{
int result = await Task.Run(async () =>
{
await Task.Delay();
return ;
});
}

此处变量result的类型正是你所期望的int,并且在该任务被调用大约1秒钟后,变量result的值被设置为42。新的await关键字被认为是等价于Unwrap方法的一种新语法形式。

int result = await Task.Factory.StartNew(async delegate
{
await Task.Delay();
return ;
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap();

这里用Unwrap重写例1那个代码片段,可以像下面使用第二个await替换Unwrap

int result = await await Task.Factory.StartNew(async delegate
{
await Task.Delay();
return ;
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

这里的await await虽然看着别扭,但是并没有问题。Task.Factory.StartNew方法返回一个Task<Task<int>>,对Task<Task<int>>使用await实际上返回Task<int>,然后再对Task<int>使用await最后返回int。

Task.Run与Task.Factory.StartNew的区别的更多相关文章

  1. C# Task.Run 和 Task.Factory.StartNew 区别

    Task.Run 是在 dotnet framework 4.5 之后才可以使用,但是 Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制.可以认 ...

  2. 【.NET】- Task.Run 和 Task.Factory.StartNew 区别

    Task.Run 是在 dotnet framework 4.5 之后才可以使用, Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制. 可以认为 ...

  3. Task.Run 和 Task.Factory.StartNew 区别

    Task.Run 是在 dotnet framework 4.5 之后才可以使用, Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制. 可以认为 ...

  4. .NET - Task.Run vs Task.Factory.StartNew

    翻译自 Stephen Toub 2011年10月24日的博文<Task.Run vs Task.Factory.StartNew>,Stephen Toub 是微软并行计算平台团队的首席 ...

  5. Task.Run Vs Task.Factory.StartNew

    在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...

  6. Task.Run Vs Task.Factory.StartNew z

    在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...

  7. Task.Run Vs Task.Factory.StartNew 【收藏】

    在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...

  8. Task.Run 和 Task.Factory.StartNew

    在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...

  9. .Net4.0如何实现.NET4.5中的Task.Run及Task.Delay方法

    前言 .NET4.0下是没有Task.Run及Task.Delay方法的,而.NET4.5已经实现,对于还在使用.NET4.0的同学来说,如何在.NET4.0下实现这两个方法呢? 在.NET4.0下, ...

随机推荐

  1. android_模拟器调试

    找到adb_server adb_server connect

  2. Python中数学函数

    1.不需要引入math模块的有: abs(),cmp(),max(),min(),pow(),round() 2.需要引入math模块的: 三角函数,及其他数学函数,fabs(), *需要特别注意: ...

  3. Mybatis映射文件的自动映射与手动映射问题

    Mapper XML 文件 MyBatis 的真正强大在于它的映射语句,也是它的魔力所在.由于它的异常强大,映射器的 XML 文件就显得相对简单.如果拿它跟具有相同功能的 JDBC 代码进行对比,你会 ...

  4. python之内置函数(一)

    一.内置函数一1.内置函数总览 abs() dict() help() min() setattr()all() dir() hex() next() slice() any() divmod() i ...

  5. Maze HDU - 4035(期望dp)

    When wake up, lxhgww find himself in a huge maze. The maze consisted by N rooms and tunnels connecti ...

  6. selenium家族发展史

    什么是Selenium? Selenium 是专门为Web应用程序编写的一个验收测试工具.Selenium测试直接运行在浏览器中,支持的浏览器包括IE(7.8.9).Mozilla Firefox.M ...

  7. 20175221 2018-2019-2 《Java程序设计》第一周学习总结

    20175221 2018-2019-2 <Java程序设计>第一周学习总结 教材学习内容总结 本周通过观看书本配套视频,学到了如解释器,编译器等一些简单概念. 还懂得了java的一些简单 ...

  8. POJ 3186 Treats for the Cows (动态规划)

    Description FJ has purchased N (1 <= N <= 2000) yummy treats for the cows who get money for gi ...

  9. Centos 7 最小化kvm部署

    1.检查CPU是否支持虚拟化 sh-4.2# grep -E '(vmx|svm)' /proc/cpuinfo # 若是无任何显示,则表示CPU不支持kvm虚拟化 2.关闭selinux sh-4. ...

  10. 分布式监控系统开发【day38】:报警自动升级代码解析及测试(八)

    一.报警自动升级代码解析 发送邮件代码 def action_email(self,action_obj,action_operation_obj,host_id,trigger_data): ''' ...