[SOLVED] Using Task.Run in ASP.Net Core

Issue

I have read these articles:

ASP.NET Core Performance Best Practices

When should I use Task.Run in Asp.Net Core?

Is it allowed to use Task.Run in an ASP.NET Core controller?

In the first article in docs.microsoft we can see this statement:

Call data access, I/O, and long-running operations APIs asynchronously
if an asynchronous API is available. Do not use Task.Run to make a
synchronous API asynchronous.

I don’t understand this statement well. I’m confused about where and when I should use Task.Run and where and when I should (can) write async web API. For example, consider this method:

public int DoSomeHeavyWork()
{
   // Consider heavy computations
   int i = 10;
   i = i * 2;
   i = i + 4;
   return i;
}

and this method:

public void SaveSomthingInDb(Order ord)
{
    _dbContext.Orders.Add(ord);
    _dbContext.SaveChange();
}

according to the above statement, I should write a web method synchronously because there is no async version:

public IActionResult API_DoSomeHeavyWork()
{
    return Ok(DoSomeHeavyWork());
}

but for method 2 I can change it this way:

public async Task SaveSomthingInDbAsync(Order ord)
{
    _dbContext.Orders.Add(ord);
    await _dbContext.SaveChangeAsync();
}

so I can write an API for that like this:

public async Task<IActionResult> API_SaveSomthingInDbAsync(Order ord)
{
    await SaveSomthingInDbAsync(ord);
    return Ok("Object Added");
}

I don’t know هs my perception of this issue is correct or not? Are write these methods true? Is there any way to run the first API async?

Thanks


Edit 1)

Thanks to "Stephen Cleary".

If we assume this sentence: We use async method in web methods of Web API to prevent blocking thread pool threads and take more requests…

Please consider this code:

await _context.Order.ToListAsync();

according to this code, there is an assembly named Microsoft.EntityFrameworkCore and it has a async method named ToListAsync:

public static Task<List<TSource>> ToListAsync<TSource>(this IQueryable<TSource> source, CancellationToken cancellationToken = default(CancellationToken))

First question is how ToListAsync implemented? Is it implemented like this?

public static Task<List<TSource>> ToListAsync<TSource>(...)
{
    Task.Run(()=>{ <Some thing>.ToList() });
}

Second question is if the method DoSomeHeavyWork placed in a separate assembly and rewrite it this way:

public Task<int> DoSomeHeavyWork()
{
   Task.Run(() => 
   {
       // Consider heavy computations
       int i = 10;
       i = i * 2;
       i = i + 4;
       return i;
   }
}

Can I then call it async from my web API and free thread pool thread?

Thanks

Solution

I’m confused about where and when I should use Task.Run

On ASP.NET, almost never. You can adopt "never" as a general rule.

and where and when I should (can) write async web API.

Your controller actions should be asynchronous if and only if they invoke asynchronous code. Generally speaking, you should prefer asynchronous APIs at the lowest level for all code that performs I/O. Then your action methods are asynchronous only if they need to call that asynchronous code.

I should write a web method synchronously because there is no async version … but for method 2 I can change it this way

Yup.

Is there any way to run the first API async?

Think of it this way: if there is synchronous (CPU-bound) work to do, then that work needs a thread. It’s already running on a thread pool thread (because web requests run on threads from thread
pool). Task.Run will move that work to a different thread pool thread, but it will still run on a thread pool thread. So Task.Run doesn’t help anything; it just adds overhead and provides no benefit. Hence the general rule of "don’t use Task.Run on ASP.NET".

Answered By – Stephen Cleary

Answer Checked By – Clifford M. (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *