c# – Why is it wrong to create asynchronous "wrappers" for synchronous methods?

Question:

I can't figure out why this code is bad:

public ICollection<Product> GetAllProducts() { //getting data from DB }

public async Task<ICollection<Product>> GetAllProductsAsync()
{
    return await Task<ICollection<Product>>.Factory.StartNew(GetAllProducts)
}

Answer:

I will not answer on my own, but simply quote the words of the .NET developers. In the end, who else but they are the ultimate truth about how something should work.

Below is a very brief content of the post "Should I expose asynchronous wrappers for synchronous methods?" from the async / await developers and the TAP creators. If you know the language, read it, if you don't know it, translate it with Google. But be sure to read it.


Asynchrony

The two main benefits of async are scalability and offloading the main thread (UI responsiveness, concurrency). Different types of applications, as a rule, need different benefits: server applications need scalability, UI applications need offload.

Scalability

The ability to run synchronous methods as asynchronous does not increase scalability because the amount of consumed resources remains the same — instead of the current thread, it runs on a different thread. Application scalability is achieved by allowing the same resources to do more work with proper asynchrony.

Unloading

In the case of increasing UI responsiveness and running code in parallel threads, wrapping synchronous code into asynchronous code is what you need.

Discussion

NET development team at the Task-based Asynchronous Pattern does not recommend keeping asynchronous methods in APIs that simply wrap synchronous ones.

As already stated, in order to improve scalability, the code must be truly asynchronous – i.e. the original synchronous code needs to be redone. In the case of unloading, the original synchronous code does not need to be altered. At the same time, if you provide only the synchronous version to the API, you get some bonuses:

  • Fewer methods means less headaches. Developers need less maintenance and testing. API users are less tormented by the choice of which method to use in each specific case.
  • According to the guideline, all methods contained in the API should be truly asynchronous. Those. By seeing an asynchronous method in an API, the API user must be confident that using it will improve scalability.
  • The choice of whether to run a synchronous method asynchronously is up to the user. If he can "be patient" / circumstances allow, he will run the method synchronously. If he needs responsiveness, he will run the method asynchronously.

Main takeaway: API should not lie and be misleading .


On my own behalf, I can only say that while you are writing programs for yourself, or while you are writing code that no one else uses, these arguments may seem empty. But believe me, in serious team development, and even more so when creating libraries that are used by many developers, these rules turn out to be very useful.

It is also worth adding that the post above is about the public API . The degree of publicity can be different – it can be a library that is installed by the hundreds from nougeta, or it can be your colleagues who use a component you developed. It is only important that the public API is not misleading.

The "async over sync is bad" statement does not in any way apply to all code. Because, for example, if asynchronous methods are used in fully synchronous code, then it is clear that until the application is completely "threaded" with async/await , the code will encounter "seams": asynchronous wrappers of synchronous methods. This is normal, this is a workflow. It is important to understand that async over sync should be either a forced measure (in the case of unloading) or a temporary measure (in the case of confusion), but not the norm .

The fact is that any thing (even a language feature, even a household appliance) should be made so that it would be easy to use it correctly and difficult to use it incorrectly. Unfortunately, the main advantage of async/await – easy writing of asynchronous code – is also a disadvantage. For example, because you can easily provide an asynchronous wrapper for synchronous code. This can result in the developer being able to provide an asynchronous wrapper with any synchronous method. If you recall the previous patterns of asynchronous programming in .NET – Asynchronous Programming Model ( BeginXXX / EndXXX ) and Event-based Asynchronous Pattern ( XXXAsync method and XXXCompleted event) – then it is much more difficult to make an asynchronous wrapper for a synchronous method in them. And the next thought after the thought "Let me make an asynchronous version as well" will be the thought "Come on, whoever needs it, they will do it themselves." Those. these models made you think carefully about whether you really need an asynchronous wrapper. When using async/await this barrier is absent, so feature developers are forced to actively talk and remind about this.

Scroll to Top