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中的方法在调用时会遵循相同的模式。
这是:
- 必要
- 业绩不利
- 只是一个偏好问题
即使这不是在单独的图层/项目中使用,如果我在相同的类中调用嵌套的方法,同样的问题也适用:
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方法,你有一个单一的await
expression式等待一个Task
或Task<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的迹象。