Question

I currently trying to figure out when to use Task.Run and when not.

In my project I'm using WebApi combined with MongoDB to store account Information.

So for the following sample code, would I be better of using the Submit or SumbitAsync method from a client call?

public class TestController : ApiController
{
    [HttpPost]
    public void Submit()
    {
        DoSave();
    }

    public async Task SubmitAsync()
    {
        await Task.Run(() => DoSave());
    }

    private void DoSave()
    {
        myMongoDbCollection.Save(new TestEntity());
    }
}

The MongoDB C# driver currently does not support async methods.

Was it helpful?

Solution

There's no point in using Task.Run here.

The point of using async I/O in ASP.NET is to free your thread, so it can handle other requests, until I/O has completed. Meanwhile, no thread will be blocked. When the I/O operation has finished, a I/O completion port will be signaled and your method will resume.

Using Task.Run, you're just deferring this to a ThreadPool thread, and making that thread block waiting for I/O.

In other words, if the client doesn't support async I/O, one of your threads will always block. So you might as well do it all synchronously and avoid unnecessary context switches.

From Stephen Cleary's blog post "Task.Run Etiquette Examples: Don't Use Task.Run in the Implementation":

There are (at least) four efficiency problems introduced as soon as you use await with Task.Run in ASP.NET:

  • Extra (unnecessary) thread switching to the Task.Run thread pool thread. Similarly, when that thread finishes the request, it has to enter the request context (which is not an actual thread switch but does have overhead).

  • Extra (unnecessary) garbage is created. Asynchronous programming is a tradeoff: you get increased responsiveness at the expense of higher memory usage. In this case, you end up creating more garbage for the asynchronous operations that is totally unnecessary.

  • The ASP.NET thread pool heuristics are thrown off by Task.Run "unexpectedly" borrowing a thread pool thread. I don't have a lot of experience here, but my gut instinct tells me that the heuristics should recover well if the unexpected task is really short and would not handle it as elegantly if the unexpected task lasts more than two seconds.

  • ASP.NET is not able to terminate the request early, i.e., if the client disconnects or the request times out. In the synchronous case, ASP.NET knew the request thread and could abort it. In the asynchronous case, ASP.NET is not aware that the secondary thread pool thread is "for" that request. It is possible to fix this by using cancellation tokens, but that's outside the scope of this blog post.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top