Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practices to use await-async, where to start the task?

I started to use the await/async mechanism in our .Net WPF application.

In my ViewModel, I'm calling an async method on a service.

My question is: Is it better to

  1. Directly inside this service, make one big return await Task.Run(()=>{...});
  2. Have all the submethods on this service being also async and then inside this have the Task.Run?

By example:

1)

public class Service:IService{
    public async Task<SomeResult>(SomeParameter parameter){
        return await Task.Run(()=>{
            CopyStuff(parameter.A);
            UpgradeStuff(parameter.B);
            return ReloadStuff(parameter.C)
        });
    }

    private void CopyStuff(ParamA parameter){
        ...//Some long operation that will mainly wait on the disk

    }
    private void UpgradeStuff(ParamB parameter){
        ...//Some long operation that should not block the GUI thread
    }
    public SomeResult ReloadStuff(ParamC parameter){
        return ...;//Some long operation that relaunch some services and return their successs      
    }   
}

2)

public class Service:IService{
    public async Task<SomeResult>(SomeParameter parameter){
        await CopyStuff(parameter.A);
        await UpgradeStuff(parameter.B);
        return await ReloadStuff(parameter.C)       
    }

    private async Task CopyStuff(ParamA parameter){
        return await Task.Run(()=>{...});//Some long operation that will mainly wait on the disk
    }
    private async Task UpgradeStuff(ParamB parameter){
        return await Task.Run(()=>{...});//Some long operation that should not block the GUI thread
    }
    public async Task<SomeResult> ReloadStuff(ParamC parameter){
        return await Task.Run(()=>{return ...});//Some long operation that relaunch some services and return their successs 
    }   
}

I can see advantages in both approaches:

  • In 1) we will use less task, this is probably most efficient(???)
  • In 2) This feel more "compliant" with the async-await approach, this would allow to change the visibility of some methods and still being async, this would allow the methods to run in parallel if required one day.
like image 608
J4N Avatar asked Oct 18 '16 09:10

J4N


1 Answers

Which option to choose?

I would use neither of your options, both of them will create an misleading API, everyone that will use your service will think that he uses async methods but the truth is that behind the false signature the methods are actually not asynchronous at all.
Your service just pushes the work to another ThreadPool thread that will be blocked during the method execution.

While in the client side that does not sound that bad in server side using this principle can really hurt your scalability.

According to Stephen Cleary:

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

You shouldn't wrap your service methods with fake async signatures if the methods are really synchronous, if you don't want to block the UI thread while the heavy method is executing you should use Task.Run when you are calling the services methods from the view model.

I suggest you to read Stephen Cleary series of Task.Run Etiquette articles in his blog.

Consider using async methods for I/O operations

Moreover I can see that the work your service does is not only CPU bound work, if so you should consider to use built in I/O async API method if there is any available over the synchronous you use now (for example Asynchronous File I/O), in this case your methods will be a true async methods and not a fake async wrappers as they are now.

If you will do it your UI thread will not block while the I/O async operation will execute, but if there is still also heavy CPU bound work involved and you don't want to block the UI during the CPU bound work execution you can still use Task.Run when you call the service method from the view model (even though the method signature is already an async one).

More about mixture of synchronous and asynchronous methods in the series of articles above.

Another great advantage of using bult in async API methods is that if the method is truly async you do not block any of the ThreadPool threads while the async I/O operation is executing and more ThreadPool threads are free to do any other work.
This is especially (but not only) important while using async programming in the server side and it can really boost your scalability.

Async and MVVM

One last thing, if you are following the MVVM pattern MSDN "async MVVM" articles are a great reading material you can use.

like image 88
YuvShap Avatar answered Oct 19 '22 19:10

YuvShap