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

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

使用这些关键字进行异步编程要容易得多,但是对套接字来说SocketAsyncEventArgs是不是很好?

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

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

async本身非常高效。 大量的工作进入了这个。

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

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

await是用一种模式设计的 – 不仅仅是Task类型 – 所以如果你需要尽可能多地挤出性能,你可以。

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

你读过 Stephen Toub的文章很棒。 我还推荐Zen of Asyncvideo (也由Stephen Toub提供)。

使用这些关键字进行异步编程要容易得多,但是对套接字来说SocketAsyncEventArgs是不是很好?

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

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

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

我不知道Mono。 在.NET上, 大多数异步I / O方法都基于完成端口。 Stream类是一个值得注意的例外。 默认情况下, Stream基类将执行“廉价包装器”,但允许派生类覆盖此行为。 来自网络通信的Stream总是覆盖它以提供真正的异步I / O. 如果流是为异步I / O显式构造的,那么处理文件的流只会覆盖它。

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

ASP.NET也有一个SynchronizationContext ,所以如果你使用的是ASP.NET,那么你已经设置好了。

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

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

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

如果您为每个请求提供了单线程SynchronizationContext (如AsyncContext ,则会将线程绑定到每个请求,即使它没有任何操作。 这几乎没有比同步multithreading服务器更好的了。

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

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

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

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