在.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
提供了八种重载方式,用于提供下面这几种组合情况:
- 普通任务(
Task
)和带返回值任务(Task<TResult>
) - 可取消任务(
Cancelable
)和不可取消任务(non-cancelabl
) - 同步委托(
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);
这里的await await
虽然看着别扭,但是并没有问题。Task.Factory.StartNew
方法返回一个Task<Task<int>>
,对Task<Task<int>>
使用await
实际上返回Task<int>
,然后再对Task<int>
使用await
最后返回int
,难道不是这样吗?
原文链接,2011年10月24日发布:
http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
Task.Run Vs Task.Factory.StartNew的更多相关文章
-
Task.Run Vs Task.Factory.StartNew z
在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...
-
Task.Run与Task.Factory.StartNew的区别
Task是可能有延迟的工作单元,目的是生成一个结果值,或产生想要的效果.任务和线程的区别是:任务代表需要执行的作业,而线程代表做这个作业的工作者. 在.Net 4中,Task.Factory.Star ...
-
C# Task.Run 和 Task.Factory.StartNew 区别
Task.Run 是在 dotnet framework 4.5 之后才可以使用,但是 Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制.可以认 ...
-
Task.Run Vs Task.Factory.StartNew 【收藏】
在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...
-
【.NET】- Task.Run 和 Task.Factory.StartNew 区别
Task.Run 是在 dotnet framework 4.5 之后才可以使用, Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制. 可以认为 ...
-
Task.Run 和 Task.Factory.StartNew
在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...
-
Task.Run 和 Task.Factory.StartNew 区别
Task.Run 是在 dotnet framework 4.5 之后才可以使用, Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制. 可以认为 ...
-
.NET - Task.Run vs Task.Factory.StartNew
翻译自 Stephen Toub 2011年10月24日的博文<Task.Run vs Task.Factory.StartNew>,Stephen Toub 是微软并行计算平台团队的首席 ...
-
.Net4.0如何实现.NET4.5中的Task.Run及Task.Delay方法
前言 .NET4.0下是没有Task.Run及Task.Delay方法的,而.NET4.5已经实现,对于还在使用.NET4.0的同学来说,如何在.NET4.0下实现这两个方法呢? 在.NET4.0下, ...
随机推荐
-
慎用mutableCopy
因为逻辑需要,我在present到一个页面时,将一个存放uiimage的数组mutablecopy了过去(因为再返回的时候防止对数组做了改动),时间长了也忘了这事儿,后来发现添加多张图片上传时,app ...
-
MyBatis入门学习教程-优化MyBatis配置文件中的配置
一.连接数据库的配置单独放在一个properties文件中 之前,我们是直接将数据库的连接配置信息写在了MyBatis的conf.xml文件中,如下: 1 <?xml version=" ...
-
处理海量数据的高级排序之——快速排序(C++)
代码实现 ...
-
Android 测试Service的生命周期
package com.example.myapp4; import android.support.v7.app.ActionBarActivity; import android.content. ...
-
进制转换器(十进制转n进制)
#include<stdio.h> #include<stdlib.h> #define MAXSIZE 100 /*链栈类型定义*/ typedef struct node ...
-
GCD XOR uvalive6657
GCD XORGiven an integer N, nd how many pairs (A; B) are there such that: gcd(A; B) = A xor B where1 ...
-
apache tomcat的下载 安装 配置
大家好!欢迎浏览我的博客 我们现在学习怎么下载,安装,配置apache-tomcat. 首先我们先了解一下Tomcat,Tomcat是Apache 软件基金会(Apache Software Foun ...
-
C# 生成小于Int数值绝对值的随机数
C#中有两种类型的随机数生成器: 伪随机数(System.Random) 安全随机数(System.Security.Cryptography.RNGCryptoServiceProvider) ...
-
element-ui el-input只显示下划线
只需要增加样式 .el-input__inner { width: 220px; border-top-width: 0px; border-left-width: 0px; border-right ...
-
Vue.js 开发环境的搭建
1. cnpm install vue-cli(安装vue 脚手架) 2. vue init webpack my-project(my-project:自定义,取一个项目的名字,init 初始化一个 ...