首页 > 代码库 > Task.Run vs Task.Factory.StartNew

Task.Run vs Task.Factory.StartNew

Task.Run 和 Task.Factory.StartNew 都可以把一段要执行的代码放到ThreadPool thread中去执行。Task.Factory.StartNew是.Net 4.0中引入的,而Task.Run则是在.Net 4.5中引入,首要目的是为了简化Task.Factory.StartNew的使用。简言之,

Task.Run(someAction)

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

是基本等价的。

这里我们说,基本等价,是因为这两种用法还是有不完全相同的地方。

考虑下面的代码

var t = Task.Factory.StartNew(delegate {        return 42; }); 

通过Task.Factory.StartNew ,我们把一个TResult是int的delegate变成了Task<int>类型。

 

那么下面的代码呢

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

传给Task.Factory.StartNew的是一个asyn的delegate,即是Task<int>类型。通过Task.Factory.StartNew,得到的t是一个Task<Task<int>>类型。

这时候,如果我们await t,实际上,当它返回时,并不是这个async的delegate执行完成了,而是得到了Task<int>。这通常不是我们想要的结果。

 

为了处理这种情况,.Net 4引入了Unwrap函数。Unwrap有两个重载方法,一个是作用于类型Task<Task>上,另外一个是作用于Task<Task<TResult>>上。

在Task<Task>上调用Unwrap会返回一个新的Task,它代表这内部Task最终是否完成。

 

回到前面的例子,如果我想让t代表内部的async delegate,那么可以这样

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

 

接下来讲Task.Run。由于上面所述的用法是一种很常见的需求,因此在.Net 4.5中,

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

上述代码就直接得到的是Task<int>,而不是Task<Task<int>>。

即该代码等价于

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

 

Task.Run vs Task.Factory.StartNew