异步/等待高性能服务器应用程序?

Kr0*_*r0e 14 c# sockets asynchronous async-await

C#5中新的async/await关键字看起来非常有前景,但我读了一篇关于性能对这些应用程序影响的文章,因为编译器将为异步方法生成一个相当复杂的状态机.

使用这些关键字进行异步编程要容易得多,但它与套接字的SocketAsyncEventArgs一样好吗?

第二个问题:像Stream.WriteAsync这样的异步IO方法是非同步的(.Net上的完成端口或Mono上的epoll/poll)还是这些方法用于将写入调用推送到线程池的廉价包装器?

第三个问题:在UI应用程序的SynchronizationContext旁边,有没有办法实现某种sinlge-threaded上下文?像事件循环之类的东西,以便在主线程上继续完成任务?我发现了Nito.AsyncEx库,但我不确定这是否是我需要的.

Ste*_*ary 32

async本身就很有效率.大量的工作进入了这个.

通常,在服务器端,您关注asyncI/O. 我将忽略asyncCPU绑定方法,因为async无论如何都会在噪声中丢失开销.

异步I/O将增加每个请求的内存使用量,但它会减少每个请求的线程使用量.所以你最终获胜(边缘病态角落病例除外).对于所有异步I/O都是如此,包括async.

await设计有一个模式 - 而不仅仅是Task类型 - 所以如果你需要尽可能多地挤出性能,你可以.

我读了一篇关于性能对这些应用程序影响的文章,因为编译器将为异步方法生成一个非常复杂的状态机.

你读过 Stephen Toub 的文章很棒.我还推荐Zen of Async视频(也由Stephen Toub提供).

使用这些关键字进行异步编程要容易得多,但它与套接字的SocketAsyncEventArgs一样好吗?

首先,要了解它SocketAsyncEventArgs更具可扩展性,因为它减少了内存垃圾.使用async套接字的简单方法会产生更多的内存垃圾,但由于await是基于模式的,你可以API 定义自己的async兼容包装器SocketAsyncEventArgs(如Stephen Toub的博客中所见......我在这里感知模式;).这可以让你挤出每一盎司的性能.

虽然从长远来看,设计一个横向扩展系统通常更好,而不是扭曲代码以避免一些内存分配.恕我直言.

第二个问题:像Stream.WriteAsync这样的异步IO方法是非同步的(.Net上的完成端口或Mono上的epoll/poll)还是这些方法用于将写入调用推送到线程池的廉价包装器?

我不知道Mono.在.NET上,大多数异步I/O方法都基于完成端口.该Stream课程是一个值得注意的例外.该Stream基类将做一个"廉价包装"默认,但允许派生类重写此行为.Stream来自网络通信的s总是覆盖它以提供真正的异步I/O. Streams表示与文件处理只能覆盖此,如果流被明确构建异步I/O.

第三个问题:在UI应用程序的SynchronizationContext旁边,有没有办法实现某种单线程上下文?

ASP.NET也有SynchronizationContext,所以如果你使用ASP.NET,你已经设置好了.

如果您正在使用自己的基于套接字的服务器(例如,Win32服务),那么您可以使用AsyncContext我的AsyncEx库中的类型.但这听起来并不像你真正想要的那样.AsyncContext将在当前线程上创建单线程上下文.但async服务器应用程序的真正威力来自扩展请求而不是线程.

考虑ASP.NET的SynchronizationContext工作原理:当每个请求进入时,它会抓取一个线程池线程并构造一个SynchronizationContext(对于该请求).当该请求具有异步工作时,它会向该寄存器注册,SynchronizationContext并且运行该请求的线程将返回到线程池.稍后,当异步工作完成时,它会抓取线程池线程(任何线程),SynchronizationContext在其上安装现有线程,并继续处理该请求.当请求最终完成时,它将SynchronizationContext被处理掉.

该过程的关键是当请求是wait(await)异步操作时,没有专用于该请求的线程.由于与线程相比,请求相当轻量级,因此服务器可以更好地扩展.

如果你给每个请求一个单线程,SynchronizationContext比如AsyncContext,这会将一个线程绑定到每个请求,即使它没有任何关系.这几乎没有比同步多线程服务器更好的了.

如果你想要发明自己的文章,SynchronizationContext你可能会发现我的MSDN文章很有用SynchronizationContext.我还在那篇文章中介绍了异步方法如何"注册"和"安装"上下文; 这主要通过自动完成async void,await因此您不必明确地执行此操作.


Ser*_*rvy 6

  1. 如果您在异步IO的上下文中使用它,那么这是一个有争议的问题.花在数据库操作,文件/网络IO等上的时间最多为毫秒.async如果没有纳秒,那么最坏的开销将是微秒.需要注意的地方是,当你有很多正在等待的操作时(如数千,数万或更多),这些操作非常快.当这些异步操作代表CPU绑定工作时,使用的开销肯定可能await至少是显而易见的.请注意,就人类的理解能力而言,为状态机生成的代码有些复杂,但状态机通常总体上表现得相当好.

  2. 这些方法不仅仅是阻塞线程池线程的包装器,不是.这会破坏所await代表的目的.这些方法不会阻塞任何线程并依赖OS挂钩来完成任务.

  3. 当然,您可以创建自己的,SynchronizationContext而不是完全依赖UI框架提供的现有. 是一个很好的例子.使用这样的东西时要小心; 它对于正确的任务来说是一个很好的工具,但是当你应该异步完成所有事情时,可能会被滥用来找到更具创造性的阻塞方法.