简体   繁体   English

使用异步/等待设计-一切都应该异步吗?

[英]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. 同样,当从接口方法(如IDisposable.Dispose )调用第A方法时-我无法使其异步。

Another example: imagine that multiple calls to A were stored as delegates. 另一个示例:假设对A多个调用被存储为委托。 Previously they were just called with _combinedDelegate.Invoke() but now I should go through GetInvocationList() and await on each item. 以前,它们只是通过_combinedDelegate.Invoke()调用的,但现在我应该遍历GetInvocationList()await每一项。

Oh, and consider also replacing property getter with async method. 哦,还要考虑用async方法替换属性getter。

I can't use Task.Wait() or .Result because: 我不能使用Task.Wait().Result ,因为:

  1. It's wasting ThreadPool threads in server app 这浪费了服务器应用程序中的ThreadPool线程
  2. It leads to deadlocks: if all ThreadPool threads are Wait ing there are no threads to complete any task. 这会导致死锁:如果所有ThreadPool线程都在Wait ,则没有线程可以完成任何任务。

So the question is: should I make absolutely all my methods initially async even if I'm not planning to call anything async inside? 所以,问题是:我应该绝对都是我的方法开始async即使我不打算叫什么异步里面? Won't it hurt performance? 会不会影响性能? Or how to design things to avoid such hard refactorings? 还是如何设计事物来避免这种艰难的重构?

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 . async设计问题与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. 以我的经验,考虑一个方法/接口/类并预测它是否将使用I / O通常很简单。 If it requires I/O, then it should probably be made task-returning. 如果需要I / O,则应该将其返回任务。 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. 有时(但并非总是如此),可以对代码进行结构化,以便在代码的自己部分中完成I / O,从而使业务对象和逻辑严格同步。 The Redux pattern in the JavaScript world is a good example of this. JavaScript世界中的Redux模式就是一个很好的例子。

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? 您是否使每个接口都从IDisposable继承并在各处using Nah, you only add it when necessary; 不,您仅在必要时添加它; and you should take the same approach with async . 并且您应该对async采用相同的方法。

Should I make absolutely all my methods initially async even if I'm not planning to call anything async inside? 即使我不打算在内部调用任何异步方法,我也应该使所有方法最初都绝对异步吗?

No, you should not. 不,你不应该。 Making everything async hurts reading, writing and understanding your code, even if only a little. 使所有内容async会损害阅读,编写和理解您的代码,即使只是一点点。 It could also hurt the performance of your code, especially if you really made every single method (does that include properties?) async . 这也可能会损害代码的性能,尤其是如果您确实使每个方法(是否包含属性?)都是async

How to design things to avoid such hard refactorings? 如何设计事物来避免这种艰难的重构?

To some degree, such refactorings might be necessary. 在某种程度上,这种重构可能是必要的。

But you can avoid a lot of the pain by structuring your code correctly using the single responsibility principle. 但是,您可以通过使用单一职责原则正确地构建代码来避免很多麻烦。 A method that checks whether state is correct should definitely not send chat messages. 检查状态是否正确的方法绝对不应发送聊天消息。 This way, changing one method to async shouldn't affect too much code. 这样,将一种方法更改为async不会影响太多代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM