5

I'm creating am ASP.NET web API where certain operations (synchronous, legacy code) might take some time (mostly IO limited, but some computationally intensive stuff too).

I understand that each incoming request is assigned a thread from the thread pool, but I'm a bit fuzzy on the rest. Is the request thread "special" in some way, warranting offloading work to other threads to avoid locking it up? Will offloading help with thread pool starvation and rejected requests? And if so, do I need to create an async wrapper for every potentially long-running function all the way down, or do I only need a single async wrapper at the highest level?

cmeeren
  • 3,890
  • 2
  • 20
  • 50
  • Even if you wrap synchronous operation in async blocks on higher level the threads that run lower level operations are blocked, which can lead to thread pool starvation. See this: https://blogs.msdn.microsoft.com/pfxteam/2012/03/24/should-i-expose-asynchronous-wrappers-for-synchronous-methods/ – hvester Mar 14 '18 at 11:27
  • Thanks, that article gave me the key phrases I needed to research this and answer the question. – cmeeren Mar 14 '18 at 12:32
  • This answer should help, it provides information on how to use queues to safely offload work from web requests: https://stackoverflow.com/questions/53171141/queuing-for-web-api-methods/57244651#57244651 – Thomas C. G. de Vilhena Jul 28 '19 at 20:26

1 Answers1

4

Creating asynchronous wrappers for long-running tasks is pointless in an ASP.NET context and does nothing but hurt performance in several ways.

Running synchronous methods asynchronously on a thread pool thread makes sense when offloading a GUI thread or another special thread. In that case, the method should be invoked asynchronously by the caller on that special thread in whichever way the caller sees fit (using e.g. Task.Run, which should generally not be used in an implementation of an async method). In ASP.NET however, there's only thread pool threads, and none need to (and indeed should not) be offloaded in this manner for several reasons.

This is put most clearly by Stephen Cleary (who, after all, wrote the book on concurrency in C#) in the blog post Task.Run Etiquette Examples: Don't Use Task.Run in the Implementation

That’s why one of the principles of ASP.NET is to avoid using thread pool threads (except for the request thread that ASP.NET gives you, of course). More to the point, this means that ASP.NET applications should avoid Task.Run.

[...]

In fact, the only place we really need an asynchronous calculation is when we call it from the UI thread.

The whole article is highly recommended and highlights various problems with offloading in ASP.NET.

Further reading:

cmeeren
  • 3,890
  • 2
  • 20
  • 50