Consider these two approaches.
Original:
public async Task<ActionResult> Foo()
{
var dataATask = _dataARepository.GetDataAsync();
var dataBTask = Task.Run(_dataBRepository.GetData());
await Task.WhenAll(dataATask, dataBTask);
var viewModel = new ViewModel(dataATask.Result, dataBTask.Result);
return View(viewModel);
}
^This version will create a new thread to call _dataBRepository.GetData()
. The additional thread will block until the call is complete. While waiting for the additional thread to complete, the main thread will yield control back to the ASP.NET pipeline, where it may handle some other user's request.
Different:
public async Task<ActionResult> Foo()
{
var dataATask = _dataARepository.GetDataAsync();
var dataBResult = _dataBRepository.GetData();
await dataATask;
var viewModel = new ViewModel(dataATask.Result, dataBResult);
return View(viewModel);
}
^This version does not spin up a separate thread for dataBRepository.GetData()
. But it does block the main thread.
So your choice is:
- Spin up another thread, just so you can yield the main thread to some other task.
- Hang on to the main thread. If some other task needs a thread, it'll have to spin up its own.
In both cases you are using one thread at a time (for the most part). In both cases the transaction will complete in the time required by the slower of the two back-end transactions. But the original option spins up a new thread and yields the current one. This seems like extra work that is not needed.
On the other hand, if the action requires two or more synchronous back-end transactions, the original option would complete faster, because they could run in parallel. Assuming that they support it. If the back-end transactions are database transactions and use the same database, they are likely to block each other, in which case you still won't see any benefit.