简体   繁体   中英

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. 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. The async one is little faster, but apart from that, what is the real benefit in implementing something like that in a 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. 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 ). Thus, the primary benefit of async on the server side is scalability .

However, you want to avoid Task.Run (and, even worse, Task.Factory.StartNew ) on ASP.NET. 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.

Using Task.Run or Task.Factory.StartNew on ASP.NET will actually decrease your scalability. 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. 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? It's just creating response object. That's it - just a fancy 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.

However, GetUsers is much more interesting. That sounds more like a data read, which is I/O-based. 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:

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. Yes, Async is faster because it frees up the request thread for the time that the operations is being performed. 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.

So for eg when you are performing a search operation on SQL server, you might want to do async and see the performance benefit.

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. Later, when the DB operation completes, a request thread is taken from the thread pool and used to continue the request.

Even if you are not using, SQL operations, let say you want to send an email to 10 people. In this case also async makes more sense.

Async is also very handy to show the progress of long event. So user will still get the active GUI, while the task is running at the background.

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. 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.

That being said, using Task.Factory.StartNew starts another thread so you don't get the scalability benefits at all. 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 . Then your code would be much more scalable. If not, you are better off sticking with the synchronous approach.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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