如何在.NET中正确使用Task对象

时间:2014-08-06 09:52:50

标签: asp.net .net asp.net-mvc-4 asp.net-mvc-5

我正在开发一个ASP .NET MVC 5应用程序,它要求我使用.NET 4.0中引入的Task对象。我正在浏览一些链接,这些链接概述了Task对象。但是,我可以使用一些帮助来检查我是否朝着正确的方向前进。

以下是Visual Studio生成的存根:

public Task<MyAppUser> FindByNameAsync(string userName) {
   throw new System.NotImplementedException();
}

我编写了一个名为mySearch()的方法,用于搜索列表。我可以将此函数用于我的实现:

public Task<MyAppUser> FindByNameAsync(string userName) {
  MyAppUser val = mySearch(userName);
  return Task<MyAppUser>.FromResult<MyAppUser>(val);
}

虽然这可行,但我认为我并没有真正正确地使用任务范例。也许我可以按如下方式编写代码:

public Task<MyAppUser> FindByNameAsync(string userName) {
  return Task<MyAppUser>.Factory.StartNew(() => mySearch(userName));
}

据我所知,我只是将一个委托作为一个Task对象返回,ASP.NET引擎将根据需要执行该对象。

我是否正确使用了任务范例?

2 个答案:

答案 0 :(得分:2)

不要从Task方法返回新的XXXAsync - 这几乎是你能做的最糟糕的事情。在您的情况下,使用Task.FromResult可能是最佳选择(如果您确实被迫使用XXXAsync方法,并且确实没有异步I / O搜索方法)。在Web应用程序中,最好同步执行整个操作,而不是在占用不同的线程时显示异步。

推理很简单 - 异步方法是节省资源的好方法。异步I / O不需要线程,因此您可以将当前线程重用于其他工作,直到数据实际就绪。在ASP.NET中,回调将被回发到ThreadPool线程,因此您已经设法基本上免费提高了吞吐量。

如果你使用Task.FromResult 伪造异步方法,那么这就失败了。但是,与WinForms或WPF不同,你并没有冻结GUI,因此通过生成新线程来掩盖缺乏异步性是没有意义的。

当你使用TaskFactory.StartNewTask.Run进行伪造时,你只会让事情变得更糟,本质上 - 你发布原始线程是正确的,就像使用正确的异步I / O一样,但是你还声称来自ThreadPool的新线程 - 所以你仍然阻止了一个线程,你只是为管道添加了一堆额外的工作。

答案 1 :(得分:2)

@ Luaan的回答非常好。我只想阐述在ASP.NET上使用async的几个原则。

1)使用同步方法签名进行同步工作。

我不确定为什么VS会生成异步存根。由于您的mySearch只是“搜索列表”(同步操作),因此您的方法应该是这样的:

public MyAppUser FindByName(string userName) {
  return mySearch(userName);
}

2)使用async / await进行异步工作(即执行I / O的任何操作)。 使用Task.Run或(甚至更糟Task.Factory.StartNew来伪造请求上下文中的异步工作。

例如,如果您需要在数据库(I / O)中搜索,那么这将是自然异步的,您应该使用异步API(例如,EF6具有异步查询):

public Task<MyAppUser> FindByNameAsync(string userName) {
  return dbContext.Users.Where(x => x.Name == userName).FirstAsync();
}

如果你计划拥有异步API但是现在你只是在做测试/存根代码,那么你应该使用FromResult

public Task<MyAppUser> FindByNameAsync(string userName) {
  return Task.FromResult(mySearch(userName));
}