Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous methods not running in parallel

I am trying to understand async/await to see if I need to make a shift in using asynchronous calls in my ASP.NET MVC 5 application.

I have the following when populating my view model with values:

MyViewModel myViewModel = new MyViewModel();
myViewModel.Answer1 = await myService.CalculateAnswer1Async();
myViewModel.Answer2 = await myService.CalculateAnswer2Async();

And the code behind the above 2 methods:

public async Task<int> CalculateAnswer1Async()
{
     await Task.Delay(5000);

     return 111;
}

public async Task<int> CalculateAnswer2Async()
{
     await Task.Delay(6000);

     return 222;
}

To my understanding the above 2 methods will execute one after the other, first CalculateAnswer1Async() and then CalculateAnswer2Async(). So the maximum time that it should take to complete this should be around 6 seconds (the time taken for CalculateAnswer2Async() to complete)? But when I run the page, it takes about 11 seconds to load, which is 5 seconds + 6 seconds = 11 seconds.

Could someone please help clarify this to me what I am doing wrong?

like image 461
Brendan Vogt Avatar asked Feb 11 '26 14:02

Brendan Vogt


2 Answers

When you await, you are pausing the current async method until that operation completes. The thread is freed to do other work, but that method will not continue executing until the await completes. I have an async intro on my blog that describes this in more detail.

If you want to do multiple asynchronous requests concurrently, then you can delay the await until all the tasks have started. So this is one option:

MyViewModel myViewModel = new MyViewModel();
var task1 = myService.CalculateAnswer1Async();
var task2 = myService.CalculateAnswer2Async();
myViewModel.Answer1 = await task1;
myViewModel.Answer2 = await task2;

It's slightly more efficient to use Task.WhenAll (and often results in clearar/cleaner code):

MyViewModel myViewModel = new MyViewModel();
var task1 = myService.CalculateAnswer1Async();
var task2 = myService.CalculateAnswer2Async();
await Task.WhenAll(task1, task2);
myViewModel.Answer1 = await task1;
myViewModel.Answer2 = await task2;

And if all the operations return the same type of value (int, in this case), then you can avoid the "spurious awaits" by using the result of Task.WhenAll:

MyViewModel myViewModel = new MyViewModel();
var task1 = myService.CalculateAnswer1Async();
var task2 = myService.CalculateAnswer2Async();
var results = await Task.WhenAll(task1, task2);
myViewModel.Answer1 = results[0];
myViewModel.Answer2 = results[1];
like image 176
Stephen Cleary Avatar answered Feb 14 '26 03:02

Stephen Cleary


You are starting the 2nd method CalculateAnswer2Async after the 1st method completes because you await the result of the 1st method.

Immediately awaiting the result of an asynchronous operation is only really useful in a UI thread since it frees the UI thread to do other work instead of blocking it. But it will not introduce concurrency.

What you want to do is start the 1st method, then the 2nd, and then call await on both results.

var result1 = myService.CalculateAnswer1Async();
var result2 = myService.CalculateAnswer2Async();
myViewModel.Answer1 = await result1;
myViewModel.Answer2 = await result2;

async/await does not introduce concurrency on its own and it's entirely possible that all the code runs synchronously. But using async and await enables you to benefit from asynchronous calls while still having code that is easy to write and that behaves mostly like you would expect from a synchronous version.

like image 23
Dirk Avatar answered Feb 14 '26 03:02

Dirk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!