Why use tasks and async await in C# inline?

119 Views Asked by At

They say, that tasks are good, cause they are non-blocking. That makes sense. Since some time, we have everything async - async action in mvc, async web api calls, async ef methods, etc. The one thing I do not understand - why? Let's assume we have a method:

public async Task<User> GetUserAsync(int id, Cancellationtoken cancellationToken = default)
{
    return await _unitOfWork.Users.SingleOrDefaultAsync(x => x.Id == id, cancellationToken);
}

What is the benefit of using task, async and wait here? To me it looks that it has no benefits at all from sync version:

public User GetUser(int id)
{
    return _unitOfWork.Users.SingleOrDefault(x => x.Id == id);
}

Let's assume this method is called from web api controller. So we have either return _usersService.GetUser(id); or return await _usersService.GetUserAsync(id); - but we wait for this async method to complete right here, not doing any work at the time it is being executed.

The only benefit of this I can see in the example below:

public async Task<User> GetUserAsync(int id, CancellatioNtoken cancellationToken = default)
{
    var userTask = _unitOfWork.Users.SingleOrDefaultAsync(x => x.Id == id, cancellationToken);
    // ... here we can do some work, some calculation for which we actually do not need user
    return await userTask;
}

In this example we can benefit from tasks and async/await since we start retrieving user, meanwhile we do some work, but in the very first example we do not have any work - we say give us a user async, but wee wait it here in place immediately.

Code like in the very first example is all over github, everybody writes it this way, and I cannot understand why

In other words, is there any sense to use Async methods with immediately waiting it? Does it differ from sync usage? If no - why people write code in this way? If yes - can you please explain me how it differ?

2

There are 2 best solutions below

0
Morten Bork On

Lets take a hypothetical:

You have 3 different API's you need to call to process information on your side.

  1. Only needs a response from the corresponding API, but the other process requires both answers before the process can finish.

    You make 1 async call to the main API, but you would like your system to also initialize the calls that need to call 2 other API's that are depending on both requests.

  2. You make 2 tasks, and do the WaitAll on the "list" of tasks that need to be completed before you continue.

    Now both processes can run simultaneously.

    The system will simply handle the 3 requests appropriately.

Async tasks are for when you need your system to handle multiple things that aren't directly correlated.

Let's say you have a queue based system that intends to get some masterdata, and then also needs to get some product data, to create an report of orders that were done over the last month. You have to have both masterdata, and product data before you continue, but lets say the masterdata is slow, like 1 second, and the product data is faster like 0.5 seconds.

If you do it synchronously, the total processing time is 1.5 seconds.

However async will in theory, and in ideal conditions "only" be as slow as the slowest thread. So a real life run time of 1 second. (Despite processing them same CPU cycles)

The benefit is, you save real world time, but allowing processors to process operations in parallel, so to speak.

At least, this is far as I understood it. I am sure there is a much more "professional and precise description". But then I will let someone else correct me on this.

4
Emperor Eto On

The short and simple answer is that using await, rather than Wait() or non-async calls, allows members of your thread pool to work on other tasks while they're waiting for something else to complete.

Think of the thread pool like your employees. Suppose one is given a task, but during the course of that task they find that they need to wait for someone else to call them back; they go any further on that task until then.

Which would you prefer - that the employee sit idle while waiting for the return phone call? Or that they pick up a different task from the queue while they're waiting? Well obviously you'd say you want them to pick up a new task.

The key here, which may not be obvious, is that, like employees, threads are not an infinite resource. You can have a whole heck of a lot, but there is a limit and adding more is not without cost. So if you didn't use await, and let every thread sit idle while waiting for the "call back", you'd either run out eventually or at best system stability would suffer.

That's really all there is to it. Your instinct is right - if threads were a free and infinite resource, there'd be no concern about blocking them and thus no need for await except in special cases like UIs where blocking a thread means blocking UI updates or user input. But even in server situations with no UI, it is always best not to block threads whenever it can be avoided. The ubiquitous use of await in modern .NET reflects this policy.

By the way - and also possibly not obvious - if you use await on a method call and it turns out there was nothing inside the method that actually required waiting on some other task, then this is perfectly harmless and nearly (but for some very very minimial overhead) identical to a purely synchronous operation. So await is one of those things that is practically never harmful, and very very often helpful.