我需要你的帮助如下。近一个月来,我一直在阅读有关任务和异步的内容。
我想在一个简单的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);
});
}
所以问题。我试过用fiddler看看这两者有什么区别。异步的速度要快一点,但除此之外,在web api中实现类似的东西有什么好处?
答案 0 :(得分:24)
正如其他人所指出的那样,ASP.NET上async
的意义在于它释放了一个ASP.NET线程池线程。这对于自然异步操作(例如I / O绑定操作)非常有用,因为这是服务器上少一个线程(没有“处理”异步操作的线程,as I explain on my blog)。因此,async
在服务器端的主要好处是可伸缩性。
但是,您希望避免在ASP.NET上使用Task.Run
(更糟糕的是,Task.Factory.StartNew
)。我称之为“假异步”,因为它们只是在线程池线程上进行同步/阻塞工作。它们在UI应用程序中非常有用,您希望将工作从UI线程推出,以便UI保持响应,但它们应该(几乎)永远不会在ASP.NET或其他服务器应用程序上使用。
在ASP.NET上使用Task.Run
或Task.Factory.StartNew
实际上会降低您的可伸缩性。它们会导致一些不必要的线程切换。对于运行时间较长的操作,最终可能会抛弃ASP.NET线程池启发式,导致创建其他线程,并在以后不必要地销毁。我一步一步地探讨这些性能问题in another blog post。
因此,您需要考虑每个操作正在做什么,以及中的任何一个是否应该异步。如果它应该,那么该动作应该是异步的。在你的情况下:
public HttpResponseMessage Get()
{
var data = _userServices.GetUsers();
return Request.CreateResponse(HttpStatusCode.OK, data);
}
Request.CreateResponse
到底在做什么?它只是创建响应对象。就是这样 - 只是一个花哨的new
。那里没有I / O,它肯定不是需要被推送到后台线程的东西。
然而,GetUsers
更有趣。这听起来更像是数据读取, 基于I / O.如果您的后端可以扩展(例如,Azure SQL / Tables / etc),那么您应该首先考虑async
,并且一旦您的服务暴露GetUsersAsync
,那么此操作可能会变为{{ 1}}:
async
答案 1 :(得分:2)
在主要IO操作发生调用的地方更有意义。 是的,Async速度更快,因为它会在执行操作时释放请求线程。因此,从Web服务器的角度来看,您将一个线程返回到池中,服务器可以使用该线程来进行任何未来的调用。
因此,例如当您在SQL Server上执行搜索操作时,您可能希望执行异步操作并查看性能优势。
这涉及多个服务器的可扩展性。
因此,例如当SearchRecordAsync将其SQL发送到数据库时,它返回一个不完整的任务,当请求到达await时,它将请求线程返回给线程池。稍后,当DB操作完成时,将从线程池中获取请求线程并用于继续请求。
即使您没有使用SQL操作,也可以说要向10个人发送电子邮件。在这种情况下,异步也更有意义。
Async对于展示长事件的进展也非常方便。因此,当任务在后台运行时,用户仍将获得活动的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;
}
}
答案 2 :(得分:1)
在服务器上使用async
可以显着提高可伸缩性,因为它可以在async
操作正在进行时释放为请求处理其他请求的线程。例如,在同步IO操作中,线程将暂停并且在操作完成之前不执行任何操作,并且无法用于提供其他请求。
话虽如此,使用Task.Factory.StartNew
启动另一个主题,因此您根本无法获得可扩展性优势。您的原始线程可以重复使用,但您已将工作卸载到另一个线程,因此根本没有净利益。事实上,转换到另一个线程的成本很高,但这是最小的。
真正的异步操作不会启动一个线程,我会查看是否存在这样的操作,或者是否可以为Request.CreateResponse
编写一个操作。那么你的代码将更具可扩展性。如果没有,你最好坚持使用同步方法。