Ver*_*ali 10 c# asynchronous task async-await asp.net-web-api
以下我需要你的帮助.近一个月来,我一直在阅读有关任务和异步的内容.
我想在一个简单的wep api项目中尝试实现我新获得的知识.我有以下方法,它们都按预期工作:
public HttpResponseMessage Get()
{
var data = _userServices.GetUsers();
return Request.CreateResponse(HttpStatusCode.OK, data);
}
public async Task<HttpResponseMessage> Get()
{
var data = _userServices.GetUsers();
return await Task<HttpResponseMessage>.Factory.StartNew(() =>
{
return Request.CreateResponse(HttpStatusCode.OK, data);
});
}
Run Code Online (Sandbox Code Playgroud)
所以问题.我试过用fiddler看看这两者有什么区别.异步的速度要快一点,但除此之外,在web api中实现类似的东西有什么好处?
Ste*_*ary 26
正如其他人所指出的那样,asyncASP.NET的重点在于它释放了一个ASP.NET线程池线程.这对于自然异步操作(例如I/O绑定操作)非常有用,因为这是服务器上少一个线程(没有线程可以"处理"异步操作,正如我在博客中解释的那样).因此,async服务器端的主要好处是可扩展性.
但是,您希望避免Task.Run(甚至更糟Task.Factory.StartNew)ASP.NET.我称之为"假异步",因为它们只是在线程池线程上进行同步/阻塞工作.它们在UI应用程序中非常有用,您希望将工作从UI线程推出,以便UI保持响应,但它们应该(几乎)永远不会在ASP.NET或其他服务器应用程序上使用.
使用Task.Run或Task.Factory.StartNew在ASP.NET上实际上会降低您的可伸缩性.它们会导致一些不必要的线程切换.对于运行时间较长的操作,最终可能会抛弃ASP.NET线程池启发式,导致创建其他线程,并在以后不必要地销毁.我在另一篇博客文章中逐步探讨了这些性能问题.
因此,您需要考虑每个操作正在做什么,以及是否应该异步.如果它应该,那么该动作应该是异步的.在你的情况下:
public HttpResponseMessage Get()
{
var data = _userServices.GetUsers();
return Request.CreateResponse(HttpStatusCode.OK, data);
}
Run Code Online (Sandbox Code Playgroud)
究竟在Request.CreateResponse做什么?它只是创建响应对象.就是这样 - 只是一种幻想new.那里没有I/O,它肯定不是需要被推送到后台线程的东西.
但是,GetUsers更有趣.这听起来更像是基于I/O 的数据读取.如果你的后端可以扩展(例如,Azure SQL/Tables/etc),那么你应该首先考虑一下async,一旦你的服务暴露了a GetUsersAsync,那么这个动作也可能变成async:
public async Task<HttpResponseMessage> Get()
{
var data = await _userServices.GetUsersAsync();
return Request.CreateResponse(HttpStatusCode.OK, data);
}
Run Code Online (Sandbox Code Playgroud)
在主要 IO 操作发生调用的地方更有意义。是的,异步更快,因为它在执行操作时释放了请求线程。因此,从 Web 服务器的角度来看,您将一个线程返回给池,服务器可以使用该线程来处理将来的任何调用。
例如,当您在 SQL Server 上执行搜索操作时,您可能希望执行异步操作并查看性能优势。
它有利于涉及多个服务器的可扩展性。
因此,例如,当 SearchRecordAsync 将其 SQL 发送到数据库时,它会返回一个未完成的任务,而当请求命中等待时,它会将请求线程返回到线程池。稍后,当数据库操作完成时,将从线程池中取出一个请求线程并用于继续请求。
即使您不使用 SQL 操作,假设您想向 10 个人发送电子邮件。在这种情况下,异步也更有意义。
异步也可以非常方便地显示长事件的进度。因此,当任务在后台运行时,用户仍然可以获得活动的 GUI。
要理解,请查看此示例。
在这里,我尝试启动名为发送邮件的任务。临时我想更新数据库,而后台正在执行发送邮件任务。
一旦数据库更新发生,它就等待发送邮件任务完成。但是,通过这种方法,很明显我可以在后台运行任务并仍然继续原始(主)线程。
using System;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
Console.WriteLine("Starting Send Mail Async Task");
Task task = new Task(SendMessage);
task.Start();
Console.WriteLine("Update Database");
UpdateDatabase();
while (true)
{
// dummy wait for background send mail.
if (task.Status == TaskStatus.RanToCompletion)
{
break;
}
}
}
public static async void SendMessage()
{
// Calls to TaskOfTResult_MethodAsync
Task<bool> returnedTaskTResult = MailSenderAsync();
bool result = await returnedTaskTResult;
if (result)
{
UpdateDatabase();
}
Console.WriteLine("Mail Sent!");
}
private static void UpdateDatabase()
{
for (var i = 1; i < 1000; i++) ;
Console.WriteLine("Database Updated!");
}
private static async Task<bool> MailSenderAsync()
{
Console.WriteLine("Send Mail Start.");
for (var i = 1; i < 1000000000; i++) ;
return true;
}
}
Run Code Online (Sandbox Code Playgroud)