I have a .net core API which has a controller that builds an aggregated object to return. the object it creates is made of data that comes from 3 method calls to a service class. These are all independent of each other and can be run in isolation from each other. Currently I am using tasks to improve the performance of this controller. the current version looks something like this...
[HttpGet] public IActionResult myControllerAction() { var data1 = new sometype1(); var data2 = new sometype2(); var data3 = new List<sometype3>(); var t1 = new Task(() => { data1 = service.getdata1(); }); t1.Start(); var t2 = new Task(() => { data2 = service.getdata2(); }); t2.Start(); var t3 = new Task(() => { data3 = service.getdata2(); }); t3.Start(); Task.WaitAll(t1, t2, t3); var data = new returnObject { d1 = data1, d2 = data2, d2 = data3 }; return Ok(data); }
This works well however I am wondering if using tasks is the best solution here? Would using async/await be a better idea and more accepted way?
For example should the controller be marked as async and an await put on each call to the the service methods?
In general, you should make a method asynchronous if the synchronous method blocks the ASP.NET request thread while doing no work. By making the call asynchronous, the ASP.NET request thread is not blocked doing no work while it waits for the web service request 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.
The async/await feature solves three performance or scalability problems: They can make your application handle more users. If you have requests that access an external resource such as a database or a web API then async frees up the thread while it is waiting.
The async and await keywordsAn asynchronous method is one that is marked with the async keyword in the method signature. It can contain one or more await statements. It should be noted that await is a unary operator — the operand to await is the name of the method that needs to be awaited.
All of these leads to improved scalability of our application. These two keywords – async and await – play a key role in asynchronous programming in ASP.NET Core. We use the async keyword in the method declaration and its purpose is to enable the await keyword within that method.
Thus with async method you can await for tasks instead of blocking thread. Instead of creating local variables and assigning them in tasks, you can use Task<T> to return results of required type.
These two keywords – async and await – play a key role in asynchronous programming in ASP.NET Core. We use the async keyword in the method declaration and its purpose is to enable the await keyword within that method. So yes, you can’t use the await keyword without previously adding the async keyword in the method declaration.
For example should the controller be marked as async and an await put on each call to the the service methods? Show activity on this post. This works well however I am wondering if using tasks is the best solution here? Would using async/await be a better idea and more accepted way? Yes, absolutely.
This works well however I am wondering if using tasks is the best solution here? Would using async/await be a better idea and more accepted way?
Yes, absolutely. Doing parallel processing on ASP.NET consumes multiple threads per request, which can severely impact your scalability. Asynchronous processing is far superior for I/O.
To use async
, first start with your lowest-level call, somewhere inside your service. It's probably doing an HTTP call at some point; change that to use asynchronous HTTP calls (e.g., HttpClient
). Then let async
grow naturally from there.
Eventually, you'll end up with asynchronous getdata1Async
, getdata2Async
, and getdata3Async
methods, which can be consumed concurrently as such:
[HttpGet] public async Task<IActionResult> myControllerAction() { var t1 = service.getdata1Async(); var t2 = service.getdata2Async(); var t3 = service.getdata3Async(); await Task.WhenAll(t1, t2, t3); var data = new returnObject { d1 = await t1, d2 = await t2, d3 = await t3 }; return Ok(data); }
With this approach, while the three service calls are in progress, myControllerAction
uses zero threads instead of four.
[HttpGet] public async Task<IActionResult> GetAsync() { var t1 = Task.Run(() => service.getdata1()); var t2 = Task.Run(() => service.getdata2()); var t3 = Task.Run(() => service.getdata3()); await Task.WhenAll(t1, t2, t3); var data = new returnObject { d1 = t1.Status == TaskStatus.RanToCompletion ? t1.Result : null, d2 = t2.Status == TaskStatus.RanToCompletion ? t2.Result : null, d3 = t3.Status == TaskStatus.RanToCompletion ? t3.Result : null }; return Ok(data); }
TaskWhenAll
to return awaitable Task object. Thus with async method you can await for tasks instead of blocking thread.Task<T>
to return results of required type.Task<TResult>.Run
methodGet
null
values for return object properties if some of tasks do not complete successfully. You can use another approach - e.g. return error if some of tasks failed.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With