asynchronous/等待架构

如果你在你的架构中使用async / await,那么是否有必要将asynchronous/等待调用“泡沫化”起来,效率不高,因为你基本上是为每一层创build一个新线程(asynchronous调用每个图层的asynchronous函数,还是不重要,只是取决于你的喜好?

我正在使用EF 6.0-alpha3,以便可以在EF中使用asynchronous方法。

我的存储库是这样的:

public class EntityRepository<E> : IRepository<E> where E : class { public async virtual Task Save() { await context.SaveChangesAsync(); } } 

现在我的业务层是这样的:

 public abstract class ApplicationBCBase<E> : IEntityBC<E> { public async virtual Task Save() { await repository.Save(); } } 

然后,当然我的UI中的方法在调用时会遵循相同的模式。

这是:

  1. 必要
  2. 业绩不利
  3. 只是一个偏好问题

即使这不是在单独的图层/项目中使用,如果我在相同的类中调用嵌套的方法,同样的问题也适用:

  private async Task<string> Dosomething1() { //other stuff ... return await Dosomething2(); } private async Task<string> Dosomething2() { //other stuff ... return await Dosomething3(); } private async Task<string> Dosomething3() { //other stuff ... return await Task.Run(() => ""); } 

如果你在你的架构中使用async / await,那么是否有必要将asynchronous/等待调用“泡沫化”起来,效率不高,因为你基本上是为每一层创build一个新线程(asynchronous调用每个图层的asynchronous函数,还是不重要,只是取决于你的喜好?

这个问题暗示了几个误区。

首先,每次调用asynchronous函数时都不要创build新的线程。

其次,你不需要声明一个asynchronous方法,只是因为你正在调用一个asynchronous函数。 如果您对已经返回的任务感到满意,只需从一个没有 async修饰符的方法中返回:

 public class EntityRepository<E> : IRepository<E> where E : class { public virtual Task Save() { return context.SaveChangesAsync(); } } public abstract class ApplicationBCBase<E> : IEntityBC<E> { public virtual Task Save() { return repository.Save(); } } 

将会稍微有效一些,因为它不涉及一个很less理由创build的状态机 – 但更重要的是,它更简单。

任何asynchronous方法,你有一个单一的awaitexpression式等待一个TaskTask<T> ,在该方法的末尾没有进一步处理,最好是不使用asynchronous/等待写入。 所以这:

 public async Task<string> Foo() { var bar = new Bar(); bar.Baz(); return await bar.Quux(); } 

最好写成:

 public Task<string> Foo() { var bar = new Bar(); bar.Baz(); return bar.Quux(); } 

(从理论上来说,创build的任务有一个很小的差别,因此主叫者可以增加延续,但在绝大多数情况下,你不会注意到任何区别。)

效率不高,因为你基本上是为每个图层创build一个新线程(为每个图层asynchronous调用一个asynchronous函数,或者它并不重要,只取决于你的偏好?

不。asynchronous方法不一定使用新线程。 在这种情况下,由于底层asynchronous方法调用是一个IO绑定方法,所以实际上不应该创build新的线程。

这是:

 1. necessary 

如果要保持asynchronous操作,则需要“asynchronous调用”asynchronous调用。 然而,这是首选,因为它可以充分利用asynchronous方法,包括在整个堆栈中组合它们。

 2. negative on performance 

不,如我所说,这不会创build新的线程。 有一些开销,但大部分可以最小化(见下文)。

 3. just a matter of preference 

不,如果你想保持这种asynchronous。 你需要做到这一点,以保持跨越堆栈asynchronous。

现在,你可以做一些事情来提高性能。 这里。 如果你只是包装一个asynchronous方法,你不需要使用语言function – 只要返回Task

 public virtual Task Save() { return repository.Save(); } 

repository.Save()方法已经返回一个Task – 你不需要等待它,只是把它包装回Task 。 这将使该方法更有效率。

您也可以让您的“低级”asynchronous方法使用ConfigureAwait来防止它们需要调用同步上下文:

 private async Task<string> Dosomething2() { //other stuff ... return await Dosomething3().ConfigureAwait(false); } 

如果您不需要担心调用上下文,这将大大减less每个await涉及的开销。 当处理“库”代码时,这通常是最好的select,因为“外部” await将捕获UI的上下文。 库的“内部”工作通常不关心同步上下文,所以最好不要捕获同步上下文。

最后,我要提防你的一个例子:

 private async Task<string> Dosomething3() { //other stuff ... // Potentially a bad idea! return await Task.Run(() => ""); } 

如果你正在使用一个asynchronous方法,在内部使用Task.Run来围绕本身不是asynchronous的东西“创buildasynchronous”,那么你就可以有效地将同步代码封装到一个asynchronous方法中。 这使用一个ThreadPool线程,但可以“隐藏”这样做的事实,有效地使API误导。 通常最好将Task.Run的调用Task.Run最高级别的调用,让底层方法保持同步,除非他们真正能够利用asynchronousIO或除Task.Run一些卸载Task.Run 。 (这并不总是正确的,但通过Task.Run包装在同步代码上的“asynchronous”代码,然后通过asynchronous/等待返回通常是一个有缺陷的devise的迹象。