我正在开发一个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引擎将根据需要执行该对象。
我是否正确使用了任务范例?
答案 0 :(得分:2)
不要从Task
方法返回新的XXXAsync
- 这几乎是你能做的最糟糕的事情。在您的情况下,使用Task.FromResult
可能是最佳选择(如果您确实被迫使用XXXAsync
方法,并且确实没有异步I / O搜索方法)。在Web应用程序中,最好同步执行整个操作,而不是在占用不同的线程时显示异步。
推理很简单 - 异步方法是节省资源的好方法。异步I / O不需要线程,因此您可以将当前线程重用于其他工作,直到数据实际就绪。在ASP.NET中,回调将被回发到ThreadPool
线程,因此您已经设法基本上免费提高了吞吐量。
如果你使用Task.FromResult
伪造异步方法,那么这就失败了。但是,与WinForms或WPF不同,你并没有冻结GUI,因此通过生成新线程来掩盖缺乏异步性是没有意义的。
当你使用TaskFactory.StartNew
或Task.Run
进行伪造时,你只会让事情变得更糟,本质上 - 你发布原始线程是正确的,就像使用正确的异步I / O一样,但是你还声称来自ThreadPool
的新线程 - 所以你仍然阻止了一个线程,你只是为管道添加了一堆额外的工作。
答案 1 :(得分:2)
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));
}