1

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

Arian
  • 12,795
  • 60
  • 169
  • 277
  • 5
    "How to make CPU intensive operation not to consume CPU (at least in a visible/measurable way)" type of questions are always entertaining... What exactly you hope to achieve by shifting CPU intensive operation from one thread to another when you likely can't control incoming flow of such requests? – Alexei Levenkov Feb 02 '22 at 21:30
  • I understand that a CPU intensive operation must use CPU. But my question is about web API and thread pool. If we can free thread pool in some way, so why we don't do that? For No.1 heavy opeation, thread from thread pool will bock until entire operation complete. – Arian Feb 03 '22 at 04:30

1 Answers1

5

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

Alexei Levenkov
  • 96,782
  • 12
  • 124
  • 169
Stephen Cleary
  • 406,130
  • 70
  • 637
  • 767
  • I'm pretty sure you could have just provided last paragraph for this specific question as OP seem to be confused where regular requests run. (+9000 for extra info) – Alexei Levenkov Feb 03 '22 at 04:43
  • @StephenCleary: Thanks dear friend. Could you please see my **Edit 1**. I want to clear these questions and help other people like me that have my questions – Arian Feb 03 '22 at 06:19
  • 1
    @Arian: No, and no. Truly asynchronous code [uses no thread](https://blog.stephencleary.com/2013/11/there-is-no-thread.html). – Stephen Cleary Feb 03 '22 at 06:42
  • @StephenCleary: Can you please show me some links an clues that guide how to convert a synchronous method to async version? Thanks – Arian Feb 04 '22 at 14:33
  • I have another question. Is it possible to write every sync method as async **truly**? – Arian Feb 04 '22 at 17:52
  • 1
    @Arian: I recommend starting at the lowest level and using asynchronous APIs there, and then letting `async` grow through the codebase. It's not possible to write every sync method as truly async; many methods are just naturally synchronous. – Stephen Cleary Feb 04 '22 at 18:30