Does long async calls make it easy?

I just converted a lot of data access methods into async for a rough draft using the following pattern, and it looks too simple to be good enough for subsequent iterations. How safe is it, what is not, and how should I do it?

A service that provides long-lasting calls:

private class UserService { public IdentityUser GetById(int id) { ... } } private UserService _userService = new UserService(); 

Original synchronous method:

 public IdentityUser GetById(int id) { return _userService.GetById(id); } 

My fantastic new async method:

 public async Task<IdentityUser> GetByIdAsync(int id) { await Task.Run(() => _userService.GetById(id)); } 
+5
source share
3 answers

You should not do fake async methods like this:

 public async Task<IdentityUser> GetByIdAsync(int id) { await Task.Run(() => _userService.GetById(id)); } 

The reason I call it “fake asynchronous” is because there is nothing asynchronous action inside the operation. In this case, you should only have a synchronous method. If the caller wants to make it asynchronous using Task.Run , he can do it.

When is something internally asynchronous? When you make a request to a web service or database, for example, there is a waiting period between sending a request and receiving a response - the request is an asynchronous operation. To avoid blocking the calling thread, use async-wait.

+5
source

Technically, this works, but it works by creating a new thread to perform a synchronous operation, which itself is wrapping and locking on its asynchronous operation. This means that you are not getting some of the biggest benefits of switching async in the first place.

The correct way is completely asynchronous. If right now you have something like this:

 private class UserService { public IdentityUser GetById(int id) { return mContext.Users.Single(u => u.Id == id); } } 

... now you should create an asynchronous version:

 private class UserService { public async Task<IdentityUser> GetByIdAsync(int id) { return await mContext.Users.SingleAsync(u => u.Id == id); } } 

Using:

 public async Task<IdentityUser> GetByIdAsync(int id) { return await _userService.GetByIdAsync(id); } 

Assuming your underlying infrastructure supports asynchronous methods, such as SingleAsync() for inherently asynchronous operations, this will allow the system to free the current thread while you wait for the database operation to complete. This thread can be reused elsewhere, and when the operation is completed, you can use any thread available at that time.

It may be worth reading and accepting these best practices . You probably want to use .ConfigureAwait(false) anywhere you don’t get access to contextual information, such as sessions and queries.

This answer assumes, of course, that GetById is inherently asynchronous: you are retrieving it from a hard drive or network location or something like that. If it calculates the user ID using long-running CPU, then Task.Run() is a good way, and you probably want to further indicate that this is a long-term task in the arguments of Task.Run() .

+2
source

Task.Run () should be used only for work with CPU. I don’t remember why. Try using the GetByIdAsync () method instead, which ultimately calls an asynchronous resource.

0
source

Source: https://habr.com/ru/post/1241064/


All Articles