Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design with async/await - should everything be async?

Assume I have an interface method implemented as

public void DoSomething(User user)  {     if (user.Gold > 1000) ChatManager.Send(user, "You are rich: " + user.Gold); } 

After some time I realize that I want to change it:

public async Task DoSomething(User user)  {     if (user.Gold > 1000) ChatManager.Send(user, "You are rich: " + user.Gold);     if (!user.HasReward)     {          using(var dbConnection = await DbPool.OpenConnectionAsync())          {              await dbConnection.Update(user, u =>                          {                             u.HasReward = true;                             u.Gold += 1000;                         });          }     } } 

I'm changing the method signature in the interface. But the calling methods were synchronous and I have to make not only them async but also the whole call tree async.

Example:

void A() {     _peer.SendResponse("Ping: " + _x.B()); }  double X.B() {     return _someCollection.Where(item => y.C(item)).Average(); }   bool Y.C(int item) {    // ...    _z.DoSomething();    return _state.IsCorrect; } 

should be changed to

async void AAsync() {     _peer.SendResponse("Ping: " + await _x.BAsync()); }  async Task<double> X.BAsync() {     // await thing breaks LINQ!     var els = new List<int>();     foreach (var el in _someCollection)     {         if (await y.CAsync(item)) els.Add(el);     }     return _els.Average(); }   async Task<bool> Y.CAsync(int item) {    // ...    await _z.DoSomething();    return _state.IsCorrect; } 

The affected call tree may be very big (many systems and interfaces) so this change is hard to do.

Also when the first A method is called from interface method like IDisposable.Dispose - I can't make it async.

Another example: imagine that multiple calls to A were stored as delegates. Previously they were just called with _combinedDelegate.Invoke() but now I should go through GetInvocationList() and await on each item.

Oh, and consider also replacing property getter with async method.

I can't use Task.Wait() or .Result because:

  1. It's wasting ThreadPool threads in server app
  2. It leads to deadlocks: if all ThreadPool threads are Waiting there are no threads to complete any task.

So the question is: should I make absolutely all my methods initially async even if I'm not planning to call anything async inside? Won't it hurt performance? Or how to design things to avoid such hard refactorings?

like image 427
Vlad Avatar asked Sep 04 '16 20:09

Vlad


People also ask

Should all methods be async?

If a method has no async operations inside it there's no benefit in making it async . You should only have async methods where you have an async operation (I/O, DB, etc.). If your application has a lot of these I/O methods and they spread throughout your code base, that's not a bad thing.

Should async methods be named async?

By convention, you append "Async" to the names of methods that have an async modifier. You can ignore the convention where an event, base class, or interface contract suggests a different name. For example, you shouldn't rename common event handlers, such as Button1_Click .

Should you always await async methods?

If a method is declared async, make sure there is an await! If your code does not have an await in its body, the compiler will generate a warning but the state machine will be created nevertheless, adding unnecessary overhead for an operation that will actually never yield.

Is async await really asynchronous?

In computer programming, the async/await pattern is a syntactic feature of many programming languages that allows an asynchronous, non-blocking function to be structured in a way similar to an ordinary synchronous function.


1 Answers

should I make absolutely all my methods initially async even if I'm not planning to call anything async inside?

This design problem with async is essentially the same as the problem with IDisposable. That is, interfaces have to predict their implementations. And this is going to be messy, no matter what you do.

In my experience, it's usually rather straightforward to consider a method/interface/class and predict whether it will use I/O or not. If it requires I/O, then it should probably be made task-returning. Sometimes (but not always), it's possible to structure your code so that I/O is done in its own section of the code, leaving business objects and logic strictly synchronous. The Redux pattern in the JavaScript world is a good example of this.

But bottom line, sometimes you make the wrong call and have to refactor. I think this is a far better approach than just making every method asynchronous. Do you make every interface inherit from IDisposable and use using everywhere? Nah, you only add it when necessary; and you should take the same approach with async.

like image 112
Stephen Cleary Avatar answered Sep 25 '22 17:09

Stephen Cleary