Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Task.Run for synchronous method in service

I've read a lot of articles about asynchronnous programming, but I'm not sure in one thing. I have 3rd party winrt library, written in C++ and I want to wrapp it. So now I have:

public Task LoginAsync(){
return Task.Run(winrtLibrary.Login();)
}

According Stephen Cleary and Stephen Toub blogs, it is not good solution. But when I use the method synchronously, my UI will not be responsive and will be blocked. Is it better to expose service method synchronously and in UI use Task.Run?

like image 362
JuP Avatar asked Jun 30 '16 07:06

JuP


Video Answer


1 Answers

What Stephen Toub means by

do not use Task.Run in the implementation of the method; instead, use Task.Run to call the method

Is that you shouldn't use Task.Run to hide CPU bound work behind async methods (Task returning methods). If you need to wrap external code, hide it behind an interface which reflects what this code do. Any asynchronous I/O can (and should) be exposed as Task returning methods, and CPU bound work must be exposed with the proper API. Let the consumers of your code to decide for themselves how to use that code. When you happen to by the consumer too, use Task.Run to run your synchronous code (now wrapped and exposed via interface) where it is very clear that you are offloading CPU bound work. In UI apps, for example, you should call Task.Run in your UI layer (and not deep down in your BL or even DA layers), where it is very clear that the UI offloads some CPU bound work.


Why do you think, that I shouldn't call Task.Run in BL? What if I have ViewModel, which references BL and BL references service layer (in my case is it wrapper).

I think that a method signature should reflect exactly what the method does. The best I can do is to redirect you back to Cleary's article:

When a developer sees two methods in an API winrtLibrary.Login() and winrtLibrary.LoginAsync(), the convention is that they represent a naturally-asynchronous operation. In other words, the developer expects that winrtLibrary.LoginAsync() is the “natural” implementation and that winrtLibrary.Login() is essentially a synchronous (blocking) equivalent of that operation. That API implies that winrtLibrary.Login will at some point have the calling thread enter a wait state as it blocks for the naturally-asynchronous operation to complete.

You can still hide synchronous code behind async method and follow Cleary's rule of thumb, if you sign your method as public Task OffloadLoginToTheThreadPool(). But I think (and apparently Cleary, too) that the alternative of simply calling Task.Run from the UI (or Controller) is a much better approach, and it follows the principles of Clean Code.

like image 59
shay__ Avatar answered Oct 30 '22 14:10

shay__