简体   繁体   English

异步任务<HttpResponseMessage>获取VS HttpResponseMessage获取

[英]async Task<HttpResponseMessage> Get VS HttpResponseMessage Get

I would need your help in the following. 以下我需要你的帮助。 For nearly a month, I have been reading regarding Tasks and async . 近一个月来,我一直在阅读有关任务和异步的内容。

I wanted to try to implement my new acquired knowledege, in a simple wep api project. 我想在一个简单的wep api项目中尝试实现我新获得的知识。 I have the following methods and both of them working as expected: 我有以下方法,它们都按预期工作:

 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);
        });
 }

So the question. 所以问题。 I have tried to use fiddler and see what is the difference between these two. 我试过用fiddler看看这两者有什么区别。 The async one is little faster, but apart from that, what is the real benefit in implementing something like that in a web api? 异步的速度要快一点,但除此之外,在web api中实现类似的东西有什么好处?

As others have pointed out, the point of async on ASP.NET is that it frees up one of the ASP.NET thread pool threads. 正如其他人所指出的,ASP.NET上的async点是它释放了一个ASP.NET线程池线程。 This works great for naturally-asynchronous operations such as I/O-bound operations because that's one less thread on the server (there is no thread that is "processing" the async operation, as I explain on my blog ). 这对于自然异步操作(例如I / O绑定操作)非常有用,因为这是服务器上少一个线程(没有线程可以“处理”异步操作, 正如我在博客中解释的那样 )。 Thus, the primary benefit of async on the server side is scalability . 因此,服务器端async的主要好处是可伸缩性

However, you want to avoid Task.Run (and, even worse, Task.Factory.StartNew ) on ASP.NET. 但是,您希望避免在ASP.NET上使用Task.Run (更糟糕的是, Task.Factory.StartNew )。 I call this "fake asynchrony" because they're just doing synchronous/blocking work on a thread pool thread. 我称之为“假异步”,因为它们只是在线程池线程上进行同步/阻塞工作。 They're useful in UI apps where you want to push work off the UI thread so the UI remains responsive, but they should (almost) never be used on ASP.NET or other server apps. 它们在UI应用程序中非常有用,您希望将工作从UI线程推出,以便UI保持响应,但它们应该(几乎)永远不会在ASP.NET或其他服务器应用程序上使用。

Using Task.Run or Task.Factory.StartNew on ASP.NET will actually decrease your scalability. 在ASP.NET上使用Task.RunTask.Factory.StartNew实际上会降低您的可伸缩性。 They will cause some unnecessary thread switches. 它们会导致一些不必要的线程切换。 For longer-running operations, you could end up throwing off the ASP.NET thread pool heuristics, causing additional threads to be created and later destroyed needlessly. 对于运行时间较长的操作,最终可能会抛弃ASP.NET线程池启发式,导致创建其他线程,并在以后不必要地销毁。 I explore these performance problems step-by-step in another blog post . 在另一篇博客文章中逐步探讨了这些性能问题。

So, you need to think about what each action is doing, and whether any of that should be async. 因此,您需要考虑每个操作正在做什么,以及是否应该异步。 If it should, then that action should be async. 如果它应该,那么该动作应该是异步的。 In your case: 在你的情况下:

public HttpResponseMessage Get()
{
  var data = _userServices.GetUsers();
  return Request.CreateResponse(HttpStatusCode.OK, data);
}

What exactly is Request.CreateResponse doing? Request.CreateResponse究竟在做什么? It's just creating response object. 它只是创建响应对象。 That's it - just a fancy new . 就是这样 - 只是一个奇特的new There's no I/O going on there, and it certainly isn't something that needs to be pushed off to a background thread. 那里没有I / O,它肯定不是需要被推送到后台线程的东西。

However, GetUsers is much more interesting. 但是, GetUsers更有趣。 That sounds more like a data read, which is I/O-based. 这听起来更像基于I / O的数据读取。 If your backend can scale (eg, Azure SQL / Tables / etc), then you should look at making that async first, and once your service is exposing a GetUsersAsync , then this action could become async too: 如果您的后端可以扩展(例如,Azure SQL / Tables / etc),那么您应该首先考虑使该async ,并且一旦您的服务暴露了GetUsersAsync ,那么此操作也可能变为async

public async Task<HttpResponseMessage> Get()
{
  var data = await _userServices.GetUsersAsync();
  return Request.CreateResponse(HttpStatusCode.OK, data);
}

It makes more sense where the call is happening with major IO operations. 在主要IO操作发生调用的地方更有意义。 Yes, Async is faster because it frees up the request thread for the time that the operations is being performed. 是的,Async速度更快,因为它会在执行操作时释放请求线程。 Thus, from Web server point of view, you are giving a thread back to the pool that can be used by the server for any future calls coming through. 因此,从Web服务器的角度来看,您将一个线程返回到池中,服务器可以使用该线程来进行任何未来的调用。

So for eg when you are performing a search operation on SQL server, you might want to do async and see the performance benefit. 因此,例如,当您在SQL服务器上执行搜索操作时,您可能希望执行异步并查看性能优势。

It is good for scalability that involves multiple servers. 它涉及多个服务器的可扩展性。

So, for eg when the SearchRecordAsync sends its SQL to the database, it returns an incomplete task, and when the request hits the await, it returns the request thread to the thread pool. 因此,例如,当SearchRecordAsync将其SQL发送到数据库时,它返回一个不完整的任务,当请求到达await时,它将请求线程返回给线程池。 Later, when the DB operation completes, a request thread is taken from the thread pool and used to continue the request. 稍后,当DB操作完成时,将从线程池中获取请求线程并用于继续请求。

Even if you are not using, SQL operations, let say you want to send an email to 10 people. 即使您没有使用SQL操作,也可以说要向10个人发送电子邮件。 In this case also async makes more sense. 在这种情况下,异步也更有意义。

Async is also very handy to show the progress of long event. Async在展示长事件的进展方面也非常方便。 So user will still get the active GUI, while the task is running at the background. 因此,当任务在后台运行时,用户仍将获得活动的GUI。

To understand, please have a look at this sample. 要了解,请查看此示例。

Here I am trying to initiate task called send mail. 在这里,我尝试启动称为发送邮件的任务。 Interim I want to update database, while the background is performing send mail task. 临时我想更新数据库,而后台正在执行发送邮件任务。

Once the database update has happened, it is waiting for the send mail task to be completed. 数据库更新发生后,它正在等待发送邮件任务完成。 However, with this approach it is quite clear that I can run task at the background and still proceed with original (main) thread. 但是,使用这种方法很明显我可以在后台运行任务并仍然继续使用原始(主)线程。

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;
    }
}

Using async on your server can dramatically improve scalability as it frees up the thread serving the request to handle other requests while the async operation is in progress. 在服务器上使用async可以显着提高可伸缩性,因为它可以在async操作正在进行时释放为请求处理其他请求的线程。 For example in a synchronous IO operaton, the thread would be suspended and doing nothing until the operation completes and would not be available to serve another request. 例如,在同步IO操作中,线程将暂停并且在操作完成之前不执行任何操作,并且无法提供其他请求。

That being said, using Task.Factory.StartNew starts another thread so you don't get the scalability benefits at all. 话虽这么说,使用Task.Factory.StartNew 启动另一个线程,所以你根本没有获得可伸缩性的好处。 Your original thread can be reused, but you have offloaded the work to another thread so there is no net benefit at all. 您的原始线程可以重复使用,但您已将工作卸载到另一个线程,因此根本没有净利益。 in fact there is a cost of switching to another thread, but that is minimal. 事实上,转换到另一个线程的成本,但这是最小的。

Truly asynchronous operations do not start a thread and I would look to see if such an operation exists, or if one can be written for Request.CreateResponse . 真正的异步操作不会启动一个线程,我会查看是否存在这样的操作,或者是否可以为Request.CreateResponse编写一个操作。 Then your code would be much more scalable. 那么你的代码将更具可扩展性。 If not, you are better off sticking with the synchronous approach. 如果没有,你最好坚持使用同步方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM