I know that this action method in the asp.net controller class can handle thousands of concurrent requests using only a few threads even if _httpClient.GetAsync takes many seconds to return. That's because HttpClient.GetAsync() (in windows) uses I/O completion ports and no thread is running until GetAsync() returns.
[HttpGet]
public async Task<object> GetData()
{
var result = await _httpClient.GetAsync("https://stackoverflow.com");
return result.StatusCode;
}
But can we say the same for async database operations? Is EF Core using some thread-less async or a new thread will be created for every await SaveChangesAsync() call?
[HttpPost]
public async Task<ActionResult<object>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return ok;
}
When the database is in some remote machine will it leverage some sockets IOCP so we can use the thread for a smaller interval (only for non-network operations in db)?
If a new thread is used for the whole time of the DB operation then I don't see any scalability improvement because for every request I finish the thread that started the Controler/Action and I just start a new (DB) one.
In Winforms or WPF we have a usability improvement because the UI needs to remain responsive but in Asp.Net Core there is no SynchronizationContext or UI to worry about.
I see async/await is used in more places in asp.net core (i.e. razor await Component.InvokeAsync) but is it really useful?
But can we say the same for async database operations?
Generally, yes. Both the HTTP client and DB query are I/O-based operations, so they don't require a local thread to just sit there waiting for the response.
Is EF Core using some thread-less async or a new thread will be created for every await SaveChangesAsync() call?
The specific implementation depends on the EF Core DB provider. Most of them are async these days but there are some notable exceptions like SQLite (and possibly Oracle? I don't remember). The common MSSQL provider is fully async, i.e., not using a separate thread behind the scenes.
When the database is in some remote machine will it leverage some sockets IOCP so we can use the thread for a smaller interval (only for non-network operations in db)?
DB queries are similar to HTTP in the sense that the client sends the query and then the DB responds with the results. There aren't any "non-network operations" from the client side.
Now, if you're asking about the DB server, then that's an implementation detail and doesn't affect how EF Core (or any other DB client) operates at all.
If a new thread is used for the whole time of the DB operation then I don't see any scalability improvement because for every request I finish the thread that started the Controler/Action and I just start a new (DB) one.
This is the exact reason why Task.Run is discouraged on ASP.NET.
I see async/await is used in more places in asp.net core (i.e. razor await Component.InvokeAsync) but is it really useful?
Razor is a bit more questionable in my mind, but that's coming from a design perspective rather than an efficiency perspective. Async razor allows the page itself to do things like query a db. I prefer to have the action perform all the async work and pass a view model to the page, which is then rendered synchronously since all the data is already in-memory. But that's just a design preference.
As a final note, async on the web server helps your web server scale. It doesn't change the scalability of your db server at all. If you only have one db server, then allowing your web server to scale probably won't buy you any scalability overall since your bottleneck is likely your db server. Async db calls benefit primarily from cloud dbs or db clusters. That said, if this is new development, then there's no reason not to make the web server async even if it's not the bottleneck. Further discussion in this article.
Related
I have a controller which calls a service method, this method calls synchronous a stored procedure
Service:
public class ServiceSomething : IServiceSomething, IService {
public int ProcessSomethingInDatabase(List<SqlParameter> sqlParameters){
IConnection connection = ConnectionFactory.GetConnection();
//This makes a synchronous call
DataTable dataTable = Connection.DalDataTable("sp_process_something", sqlParameters);
int result = GetResultFromDataTable(dataTable);
return result;
}
}
Controller:
public class SomethingController : Controller {
private readonly IServiceSomething _serviceSomething;
[HttpPost]
public async Task<CustomResult> ProcessSomethingInDatabase(Criteria criteria){
List<SqlParameter> sqlParameters = CriteriaToSqlParams(criteria);
int result = await Task.Run(() => _serviceSomething.ProcessSomethingInDatabase(sqlParameters));
return new CustomResult(result);
}
}
That process could take a long time (from 30 seconds to 1 hour on some occasions).
The problem is that the synchronous call freezes the application, and because of that we use Task.Run. It has been requested to me that the thread don't be initialized in the controller.
Now I would like to know what is the best implementation, so much as not to freeze the application and to handle a process that can take hours to finish.
Should I really create a thread for this?
public async Task<int> ProcessSomethingInDatabaseAsync(List<SqlParameter> sqlParameters){
IConnection connection = ConnectionFactory.GetConnection();
return await Task.Run(() => {
DataTable dataTable = Connection.DalDataTable("sp_process_something", sqlParameters);
int result = GetResultFromDataTable(dataTable);
return result;
});
}
And the controller be
[HttpPost]
public async Task<CustomResult> ProcessSomethingInDatabase(Criteria criteria){
List<SqlParameter> sqlParameters = CriteriaToSqlParams(criteria);
int result = await _serviceSomething.ProcessSomethingInDatabaseAsync(sqlParameters);
return new CustomResult(result);
}
or should I use Task.FromResult?
public Task<int> ProcessSomethingInDatabaseAsync(List<SqlParameter> sqlParameters){
IConnection connection = ConnectionFactory.GetConnection();
DataTable dataTable = Connection.DalDataTable("sp_process_something", sqlParameters);
int result = GetResultFromDataTable(dataTable);
return Task.FromResult(result);
}
Note:
The service is hosted on a Windows Service and it is communicated through WCF
The short answer to your question is none of them, let's see why.
There are at least a couple of issues in the way you are trying to design your solution.
First of all you claimed that the operation you are trying to implement could take up until 1 hour of processing. This means that you must not execute that operation in the context of an HTTP request. HTTP requests are meant to be quick, any operation that can take a time greater than a few seconds should not be implemented via HTTP. Web clients, web servers and web infrastructure are all designed for quick processing of HTTP requests and there are timeouts everywhere which won't allow you to perform your operation inside of an HTTP request.
You can use an HTTP request to ask your backend to perform a long running operation. Your web stack will process the request and will decide whether or not the task that you are requesting can be started (based on your business rules), but that's it: the actual execution of the task must be delegated to backend services (for instance by using a queue).
This is a large topic, but I hope you get the idea: you can't use an action method to perform a long running operation; your action method should only validate the request to execute the operation and delegate the actual execution to someone else. You can read this blog post to get more information about this approach.
The second point which needs attention is the usage of Task.Run. The only valid reason to use Task.Runis calling CPU-bound workload from a UI thread (for instance calling a long running CPU-bound workload from an event handler of a windows form application). That's it. If you are in a scenario other than this one you should avoid the usage of Task.Run. If you have an operation which is asynchronous in nature, such as a database query, you should use an asynchronous api to execute that operation and await the result inside of an async method. In modern .NET code there is full support for asynchronous apis, so you can always use them to perform IO operations. If you are using a data access library which doesn't offer asynchronous apis, then change your data access library.
The usage of Task.Runis particularly dangerous in the context of ASP.NET applications. In ASP.NET applications you have a pool of threads which are used by the runtime to handle incoming HTTP requests. Each time you call Task.Run you are borrowing one of this threads to execute a workload (represented by the delegate that you pass to Task.Run). You are not expected to do that, because threads are an important resource in a web server and they should be used to serve incoming HTTP requests and handled by the ASP.NET runtime. By borrowing one threads for your Task.Run execution you are interfering with the ASP.NET thread pool management and you should not do that.
To summarize, if you are writing ASP.NET code:
use asynchronous apis each time you need to perform a workload which is truly asynchronous, such as a database query or an HTTP request to a web service. await the result of the asynchronous operation inside of an async method and never block on asynchronous operations using apis such as Task.Wait() and Task.Result
if you need to perform a CPU-bound synchronous workload inside of an action method, simply do that and do not fake asynchrony by wrapping your method call by using Task.Run. Generally speaking never use Task.Run in ASP.NET code: the ony place where it makes sense is UI client applications, such as windows forms or WPF applications. Use Task.Run each time you need to call long running cpu-bound workload from a UI thread, so that you do not freeze the application UI.
never execute operations which can last more than a few seconds inside of HTTP requests. HTTP requests are meant to be processed quickly. Use a queue mechanism to delegate to backend services the execution of long running tasks.
Consider reading this blog post for more information about the usage of Task.Run.
A final note on Task.FromResult<T>, that you mentioned in your question. This method is meant to create a Task instance representing a successfully completed asynchronous operation, having a specified result. That's it. This is not a way to fake asynchrony (generally speaking you should avoid faking asynchrony), it's just a way to solve the problem of creating a Task instance for an already completed operation producing a certain result.
I know you should only use async for stuff which is not "CPU-intensive", e.g. file writes, web calls etc. and therefore I also know it doesn't makes sense to wrap every method into Task.Run or something similar.
However what should I do when I know a method does a web call, but it doesn't offer an async interface. Is it in this case worth to wrap it?
Concrete example:
I'm using CSOM (Client SharePoint Object Model) in my WebApi application (server) and want to get a SharePoint list.
This is normally done like this:
[HttpGet]
[Route("foo/{webUrl}")]
public int GetNumberOfLists(string webUrl)
{
using (ClientContext context = new ClientContext(webUrl))
{
Web web = context.Web;
context.Load(web.Lists);
context.ExecuteQuery();
return web.Lists.Count;
}
}
And I thought about changing it to something like this:
[HttpGet]
[Route("foo/{webUrl}")]
public async Task<int> GetNumberOfLists(string webUrl)
{
using (ClientContext context = new ClientContext(webUrl))
{
Web web = context.Web;
context.Load(web.Lists);
await Task.Run(() => clientContext.ExecuteQuery());
return web.Lists.Count;
}
}
Does it make sense and does it help? As I understand it, I just create / need a new thread for executing the query ("overhead") but at least the request thread will be free / ready for another request (that would be good).
But is it worth it and should it be done like this?
If so:
Isn't it strange that Microsoft doesn't offer the "async" method out of the box or did they just not care about it?
edit:
updated to use Task.Run as suggested in comment.
However what should I do when I know a method does a web call, but it doesn't offer an async interface.
Unfortunately still somewhat common. As different libraries update their APIs, they will eventually catch up.
Is it in this case worth to wrap it?
Yes, if you're dealing with a UI thread. Otherwise, no.
Concrete example... in my WebApi application (server)
Then, no, you don't want to wrap in Task.Run. As noted in my article on async ASP.NET:
You can kick off some background work by awaiting Task.Run, but there’s no point in doing so. In fact, that will actually hurt your scalability by interfering with the ASP.NET thread pool heuristics... As a general rule, don’t queue work to the thread pool on ASP.NET.
Wrapping with Task.Run on ASP.NET:
Interferes with the ASP.NET thread pool heuristics twice (by taking a thread now and then releasing it later).
Adds overhead (code has to switch threads).
Does not free up a thread (the total number of threads used for this request is almost equal to just calling the synchronous version).
As I understand it, I just create / need a new thread for executing the query ("overhead") but at least the request thread will be free / ready for another request (that would be good).
Yes, but all you're doing is jumping threads, for no benefit. The thread used to block on the query result is one less thread ASP.NET has to use to handle requests, so freeing up one thread by consuming another isn't a good tradeoff.
Isn't it strange that Microsoft doesn't offer the "async" method out of the box or did they just not care about it?
Some of the "older" MS APIs just haven't gotten around to adding async versions yet. They certainly should, but developer time is a finite resource.
This is my personal view of your problem and for me the above way is not required. When we host your API in IIS, the server assigns one thread from thread pool it has in the server. The IIS also has a setting of maxConcurrentRequestsPerCPU maxConcurrentThreadsPerCPU. You can setup these values to serve the request instead of handling the request all by yourself.
This question already has answers here:
When should I use Async Controllers in ASP.NET MVC?
(8 answers)
Closed 6 years ago.
I am working with a pre-existing C# ASP.NET MVC webapplication and I'm adding some functionality to it that I can't decide whether or not to make async.
Right now, the application home page just processes a page and a user logs in. Nothing more, and nothing asynchronous going on at all.
I am adding functionality that will, when the homepage is visited, generate a call to a Web API that subsequently calls a database that grabs an identifier and returns it to an HTML tag on the home page. This identifier will not be visible on the screen, only on the source/HTML view (this is being added for various internal tracking purposes).
The Web API/database call is simple, just grab an identifier and return it to the controller. Regardless, I'm wondering whether the app should make this call asynchronously? The website traffic isn't immense, but I'm still wondering about concurrency, performance and future scalability.
The one catch is that I'd have to make the entire ActionMethod async and I'm not sure what the affects of that would be. The basic pattern, currently synchronous, is below:
public ActionResult Index()
{
var result = GetID();
ViewBag.result = result.Data;
return View();
}
public JsonResult GetID()
{
var result = "";
var url = "http://APIURL/GetID";
using (WebClient client = new WebClient())
{
result = client.DownloadString(url);
}
return Json(result, JsonRequestBehavior.AllowGet);
}
Any thoughts?
First and foremost, realize the purpose of async, in the context of a web application. A web server has what's called a thread pool. Generally speaking, 1 thread == 1 request, so if you have a 1000 threads in the pool (typical), your website can roughly serve 1000 simultaneous requests. Also keep in mind that, it often takes many requests to render a single resource. The HTML document itself is one request, but each image, JS file, CSS file, etc. is also a request. Then, there's any AJAX requests the page may issue. In other words, it's not uncommon for a request for a single resource to generate 20+ requests to the web server.
Given that, when your server hits its max requests (all threads are being utilized), any further requests are queued and processed in order as threads are made available. What async does is buy you some additional head room. If there's threads that are in a wait-state (waiting for the results of a database query, the response from a web service, a file to be read from the filesystem, etc.), then async allows these threads to be returned to the pool, where they are then able to field some of those waiting requests. When whatever the thread was waiting on completes, a new thread is requested to finish servicing the request.
What is important to note here is that a new thread is requested to finish servicing the request. Once the thread has been released to the pool, you have to wait for a thread again, just like a brand new request. This means running async can sometimes take longer than running sync, depending on the availability of threads in the pool. Also, async caries with it a non-insignificant amount of overhead that also adds to the overall load time.
Async != faster. It can many times be slower, but it allows your web server to more efficiently utilize resources, which could mean the difference between falling down and gracefully bearing load. Because of this, there's no one universal answer to a question like "Should I just make everything async?" Async is a trade-off between raw performance and efficiency. In some situations it may not make sense to use async at all, while in others you might want to use it for everything that's applicable. What you need to do is first identity the stress points of your application. For example, if your database instance resides on the same server as your web server (not a good idea, BTW), using async on your database queries would be fairly pointless. The chief culprit of waiting is network latency, whereas filesystem access is typically relatively quick. On the other hand, if your database server is in a remote datacenter and has to not only travel the pipes between there and your web server but also do things like traverse firewalls, well, then your network latency is much more significant, and async is probably a very good idea.
Long and short, you need to evaluate your setup, your environment and the needs of your application. Then, and only then, can you make smart decisions about this. That said, even given the overhead of async, if there's network latency involved at all, it's a pretty safe bet async should be used. It's perfectly acceptable to err on the site of caution and just use async everywhere it's applicable, and many do just that. If you're looking to optimize for performance though (perhaps you're starting the next Facebook?), then you'd want to be much more judicious.
Here, the reason to use async IO is to not have many threads running at the same time. Threads consume OS resources and memory. The thread pool also cal be a little slow to adjust to sudden load. If your thread count in a web app is below 100 and load is not extremely spikey you have nothing to worry about.
Generally, the slower a web service and the more often it is called the more beneficial async IO can be. You will need on average (latency * frequency) threads running. So 100ms call time and 10 calls per second is about 1 thread on average.
Run the numbers and see if you need to change anything or not.
Any thoughts?
Yes, lot's of thoughts...but that alone doesn't count as an answer. ;)
There is no real good answer here since there isn't much context provided. But let's address what we know.
Since we are a web application, each request/response cycle has a direct impact on performance and can be a bottleneck.
Since we are internally invoking another API call from ours, we shouldn't assume that it is hosted on the same server - as such this should be treated just like all I/O bound operations.
With the two known factors above, we should make our calls async. Consider the following:
public async Task<ActionResult> Index()
{
var result = await GetIdAsync();
ViewBag.result = result.Data;
return View();
}
public async Task<JsonResult> GetIdAsync()
{
var result = "";
var url = "http://APIURL/GetID";
using (WebClient client = new WebClient())
{
// Note the API change here?
result = await client.DownloadStringAsync(url);
}
return Json(result, JsonRequestBehavior.AllowGet);
}
Now, we are correctly using async and await keywords with our Task<T> returning operations. This will help to ensure ideal performance. Notice the API change on the client.DownloadStringAsync too, this is very important.
I have some concerns using async actions in ASP.NET MVC. When does it improve performance of my apps, and when does it not?
Is it good to use async action everywhere in ASP.NET MVC?
Regarding awaitable methods: shall I use async/await keywords when I want to query a database (via EF/NHibernate/other ORM)?
How many times can I use await keywords to query the database asynchronously in one single action method?
You may find my MSDN article on the subject helpful; I took a lot of space in that article describing when you should use async on ASP.NET, not just how to use async on ASP.NET.
I have some concerns using async actions in ASP.NET MVC. When it improves performance of my apps, and when - not.
First, understand that async/await is all about freeing up threads. On GUI applications, it's mainly about freeing up the GUI thread so the user experience is better. On server applications (including ASP.NET MVC), it's mainly about freeing up the request thread so the server can scale.
In particular, it won't:
Make your individual requests complete faster. In fact, they will complete (just a teensy bit) slower.
Return to the caller/browser when you hit an await. await only "yields" to the ASP.NET thread pool, not to the browser.
First question is - is it good to use async action everywhere in ASP.NET MVC?
I'd say it's good to use it everywhere you're doing I/O. It may not necessarily be beneficial, though (see below).
However, it's bad to use it for CPU-bound methods. Sometimes devs think they can get the benefits of async by just calling Task.Run in their controllers, and this is a horrible idea. Because that code ends up freeing up the request thread by taking up another thread, so there's no benefit at all (and in fact, they're taking the penalty of extra thread switches)!
Shall I use async/await keywords when I want to query database (via EF/NHibernate/other ORM)?
You could use whatever awaitable methods you have available. Right now most of the major players support async, but there are a few that don't. If your ORM doesn't support async, then don't try to wrap it in Task.Run or anything like that (see above).
Note that I said "you could use". If you're talking about ASP.NET MVC with a single database backend, then you're (almost certainly) not going to get any scalability benefit from async. This is because IIS can handle far more concurrent requests than a single instance of SQL server (or other classic RDBMS). However, if your backend is more modern - a SQL server cluster, Azure SQL, NoSQL, etc - and your backend can scale, and your scalability bottleneck is IIS, then you can get a scalability benefit from async.
Third question - How many times I can use await keywords to query database asynchronously in ONE single action method?
As many as you like. However, note that many ORMs have a one-operation-per-connection rule. In particular, EF only allows a single operation per DbContext; this is true whether the operation is synchronous or asynchronous.
Also, keep in mind the scalability of your backend again. If you're hitting a single instance of SQL Server, and your IIS is already capable of keeping SQLServer at full capacity, then doubling or tripling the pressure on SQLServer is not going to help you at all.
Asynchronous action methods are useful when an action must perform several independent long running operations.
A typical use for the AsyncController class is long-running Web
service calls.
Should my database calls be asynchronous ?
The IIS thread pool can often handle many more simultaneous blocking requests than a database server. If the database is the bottleneck, asynchronous calls will not speed up the database response. Without a throttling mechanism, efficiently dispatching more work to an overwhelmed database server by using asynchronous calls merely shifts more of the burden to the database. If your DB is the bottleneck, asynchronous calls won’t be the magic bullet.
You should have a look at 1 and 2 references
Derived from #PanagiotisKanavos comments:
Moreover, async doesn't mean parallel. Asynchronous execution frees a
valuable threadpool thread from blocking for an external resource, for
no complexity or performance cost. This means the same IIS machine can
handle more concurrent requests, not that it will run faster.
You should also consider that blocking calls start with a
CPU-intensive spinwait. During stress times, blocking calls will
result in escalating delays and app pool recycling. Asynchronous calls
simply avoid this
is it good to use async action everywhere in ASP.NET MVC?
As usual in programming, it depends. There is always a trade-off when going down a certain path.
async-await shines in places where you know you'll receiving concurrent requests to your service and you want to be able to scale out well. How does async-await help with scaling out? In the fact that when you invoke a async IO call synchronously, such as a network call or hitting your database, the current thread which is responsible for the execution is blocked waiting for the request to finish. When you use async-await, you enable the framework to create a state machine for you which makes sure that after the IO call is complete, your method continues executing from where it left off.
A thing to note is that this state machine has a subtle overhead. Making a method asynchronous does not make it execute faster, and that is an important factor to understand and a misconception many people have.
Another thing to take under consideration when using async-await is the fact that it is async all the way, meaning that you'll see async penetrate your entire call stack, top to buttom. This means that if you want to expose synchronous API's, you'll often find yourself duplicating a certain amount of code, as async and sync don't mix very well.
Shall I use async/await keywords when I want to query database (via
EF/NHibernate/other ORM)?
If you choose to go down the path of using async IO calls, then yes, async-await will be a good choice, as more and more modern database providers expose async method implementing the TAP (Task Asynchronous Pattern).
How many times I can use await keywords to query database
asynchronously in ONE single action method?
As many as you want, as long as you follow the rules stated by your database provider. There is no limit to the amount of async calls you can make. If you have queries which are independent of each other and can be made concurrently, you can spin a new task for each and use await Task.WhenAll to wait for both to complete.
async actions help best when the actions does some I\O operations to DB or some network bound calls where the thread that processes the request will be stalled before it gets answer from the DB or network bound call which you just invoked. It's best you use await with them and it will really improve the responsiveness of your application (because less ASP input\output threads will be stalled while waiting for the DB or any other operation like that). In all my applications whenever many calls to DB very necessary I've always wrapped them in awaiatable method and called that with await keyword.
My 5 cents:
Use async/await if and only if you do an IO operation, like DB or external service webservice.
Always prefer async calls to DB.
Each time you query the DB.
P.S. There are exceptional cases for point 1, but you need to have a good understanding of async internals for this.
As an additional advantage, you can do few IO calls in parallel if needed:
Task task1 = FooAsync(); // launch it, but don't wait for result
Task task2 = BarAsync(); // launch bar; now both foo and bar are running
await Task.WhenAll(task1, task2); // this is better in regard to exception handling
// use task1.Result, task2.Result
As you know, MVC supports asynchronous controllers and you should take advantage of it. In case your Controller, performs a lengthy operation, (it might be a disk based I/o or a network call to another remote service), if the request is handled in synchronous manner, the IIS thread is busy the whole time. As a result, the thread is just waiting for the lengthy operation to complete. It can be better utilized by serving other requests while the operation requested in first is under progress. This will help in serving more concurrent requests.
Your webservice will be highly scalable and will not easily run into C10k problem.
It is a good idea to use async/await for db queries. and yes you can use them as many number of times as you deem fit.
Take a look here for excellent advise.
My experience is that today a lot of developers use async/await as a default for controllers.
My suggestion would be, use it only when you know it will help you.
The reason is, as Stephen Cleary and others already mentioned, it can introduce performance issues, rather than resolving them, and it will help you only in a specific scenario:
High-traffic controllers
Scalable backend
Is it good to use async action everywhere in ASP.NET MVC?
It's good to do so wherever you can use an async method especially when you have performance issues at the worker process level which happens for massive data and calculation operations. Otherwise, no need because unit testing will need casting.
Regarding awaitable methods: shall I use async/await keywords when I
want to query a database (via EF/NHibernate/other ORM)?
Yes, it's better to use async for any DB operation as could as possible to avoid performance issues at the level of worker processes.
Note that EF has created many async alternatives for most operations, such as:
.ToListAsync()
.FirstOrDefaultAsync()
.SaveChangesAsync()
.FindAsync()
How many times can I use await keywords to query the database
asynchronously in one single action method?
The sky is the limit
I'm trying to benchmark (using Apache bench) a couple of ASP.NET Web API 2.0 endpoints. One of which is synchronous and one async.
[Route("user/{userId}/feeds")]
[HttpGet]
public IEnumerable<NewsFeedItem> GetNewsFeedItemsForUser(string userId)
{
return _newsFeedService.GetNewsFeedItemsForUser(userId);
}
[Route("user/{userId}/feeds/async")]
[HttpGet]
public async Task<IEnumerable<NewsFeedItem>> GetNewsFeedItemsForUserAsync(string userId)
{
return await Task.Run(() => _newsFeedService.GetNewsFeedItemsForUser(userId));
}
After watching Steve Sanderson's presentation I issued the following command ab -n 100 -c 10 http://localhost.... to each endpoint.
I was surprised as the benchmarks for each endpoint seemed to be approximately the same.
Going off what Steve explained I was expecting that the async endpoint would be more performant because it would release thread pool threads back to the thread pool immediately, thus making them available for other requests and improving throughput. But the numbers seem exactly the same.
What am I misunderstanding here?
Using await Task.Run to create "async" WebApi is a bad idea - you will still use a thread, and even from the same thread pool used for requests.
It will lead to some unpleasant moments described in good details here:
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.
Basically, you do not allow any asynchrony to the ASP.NET - you just hide the CPU-bound synchronous code behind the async facade. Async on its own is ideal for I/O bound code, because it allows to utilize CPU (threads) at their top efficiency (no blocking for I/O), but when you have Compute-bound code, you will still have to utilize CPU to the same extent.
And taking into account the additional overhead from Task and context switching you will get even worser results than with simple sync controller methods.
HOW TO MAKE IT TRULY ASYNC:
GetNewsFeedItemsForUser method shall be turned into async.
[Route("user/{userId}/feeds/async")]
[HttpGet]
public async Task<IEnumerable<NewsFeedItem>> GetNewsFeedItemsForUserAsync(string userId)
{
return await _newsFeedService.GetNewsFeedItemsForUser(userId);
}
To do it:
If it is some library method then look for its async variant (if there are none - bad luck, you'll have to search for some competing analogue).
If it is your custom method using file system or database then leverage their async facilities to create async API for the method.