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

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

Task.Run(someAction);

实际上等价于:

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

使用这些默认参数,Task.Run就能用于大多数情况——只是将任务简单的交给后台线程池去执行(这也是使用TaskScheduler.Default参数的目标)。这也并不意味着Task.Factory.StartNew方法就不必再使用了,它仍然有很多重要的用处。你可以通过控制TaskCreationOptions参数来控制任务的行为,你也可以通过控制TaskScheduler来控制任务应该如何排队如何运行,你也可以使用重载方法中的接受对象状态那个参数,对于一些性能敏感的代码,它可以用于避免闭包以及相应的资源分配。不过对于上面那个简单的例子,Task.Run是最友好。

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

  1. 普通任务(Task)和带返回值任务(Task<TResult>
  2. 可取消任务(Cancelable)和不可取消任务(non-cancelabl
  3. 同步委托(Synchronous)和异步委托(Asynchronous

前两个很明显,对于第一点如果是用的Task做返回值的重载方法,那么该任务就没有返回值,如果是用的Task<TResult>做返回值的重载方法,那么该任务就有一个类型为TResult的返回值。对于第二点,也有接受CancellationToken参数的重载,可以在任务开始之前执行取消操作,然后并行任务(Task Parallel Library——TPL)就可以自然的过度到取消状态。
第三点要更有趣一些,它直接关系到Visual studio 11中的C#和Visual Basic的异步语言支持。我们先使用Task.Factory.StartNew来展示下这个问题,如果有下面一段代码:

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(() => 42));
return inner;
});

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

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

这里使用了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(1000);
return 42;
}).Unwrap();

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

现在回到Task.Run,因为微软想让开发者尽可能的使用这个方法来启用后台任务,并且可以配合async/await使用,所以微软决定在Task.Run方法中内建unwrapping的功能。这也是上面第三点所指的内容,Task.Run的重载方法中有可以接受Action(没有返回值的任务)的,有接受Func<TResult>(返回TResult的任务)的,有接受Func<Task>(返回一个异步任务的任务)的,还有接受Func<Task<TResult>>(返回一个带TResult类型返回值的异步任务的任务)的。总的来说,Task.Run方法提供了上面Task.Factory.StartNew方法相同的unwrapping操作。于是,我们可以这样写:

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

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

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

如前所述,这是一个快捷方式。

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

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

此处变量result的类型正是你所期望的int,并且在该任务被调用大约1秒钟后,变量result的值被设置为42。

有趣的是,新的await关键字被认为是等价于Unwrap方法的一种新语法形式。于是,如果我们回到上面那个Task.Factory.StartNew例子,我们可以先用Unwrap重写上面那个代码片段:

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

或者,可以使用第二个await替换Unwrap

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

Task.Run Vs Task.Factory.StartNew 【收藏】的更多相关文章

  1. Task.Run Vs Task.Factory.StartNew

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

  2. Task.Run Vs Task.Factory.StartNew z

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

  3. Task.Run与Task.Factory.StartNew的区别

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

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

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

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

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

  6. Task.Run 和 Task.Factory.StartNew

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

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

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

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

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

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

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

随机推荐

  1. Linux 第七天

    软件包管理 1.软件包分类 1)源码包(脚本安装包) 优点: l  开源,如果有足够的能力,可以修改源代码 l  可以自由选择所需的功能 l  软件是编译安装,所以更加适合自己的系统,更加稳定也效率更 ...

  2. drf1 rest & restful规范

    web服务交互 我们在浏览器中能看到的每个网站,都是一个web服务.那么我们在提供每个web服务的时候,都需要前后端交互,前后端交互就一定有一些实现方案,我们通常叫web服务交互方案. 目前主流的三种 ...

  3. 跨域访问问题js

    您可以通过使用JSONP形式的回调函数来加载其他网域的JSON数据 访问百度的地址解析  返回来省,地区,市 $.getJSON("http://api.map.baidu.com/clou ...

  4. 21.ArrayList

    ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部用来 ...

  5. 关于Runtime.getRuntime().exec()产生阻塞的2个陷阱

    本文来自网易云社区 背景 相信做java服务端开发的童鞋,经常会遇到Java应用调用外部命令启动一些新进程来执行一些操作的场景,这时候就会使用到Runtime.getRuntime().exec(), ...

  6. githug rename_commit 修改已经commit但还没push的一条message

    githug 第 45 关, 一开始对 git rebase -i 这个东西有误解, 记录一下正确的用法 ddmobadeMac-mini:git_hug ddmoba$ githug reset 4 ...

  7. 第二十四节:Java语言基础-讲解数组的综合应用

    数组的综合应用 // 打印数组 public static void printArray(int[] arr) { for(int x=0;x<arr.length;x++) { if(x!= ...

  8. 用react+redux写一个todo

    概述 最近学习redux,打算用redux写了一个todo.记录下来,供以后开发时参考,相信对其他人也有用. 代码 代码请见我的github 组织架构如下图:

  9. 有没有人曾告诉你,你的SQL又报错了?[开发篇]

    引语:线上运行的真实环境总是变幻莫测,明明你在本地测试的时候各种情况都是OK得不要不要的,也许你还在为自己某个地方炫酷的效果以及神奇的设计感到激动不已!但是,到线上以后,他就会偶尔跟抽风一样的跟你sa ...

  10. location匹配

    =/ 表示精确匹配  www.sensetime.com/ ~ :表示做正则表达式匹配,区分字符大小写 ~* : 表示做正则表达式匹配,不区分大小写 ^~: URI的左半部分匹配,不区分大小写 匹配优 ...