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

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

使用这些关键字的异步编程非常容易,但是与套接字的SocketAsyncEventArgs一样好?

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

第三个问题:在UI应用程序的SynchronizationContext旁边,是否有一种方法来实现某种单线程上下文? 就像一个事件循环,以便完成的任务继续在主线程上? 我发现了Nito.AsyncEx库,但我不太确定这是否是我需要的。


async本身是非常高效的。 一大堆工作进入了这一步。

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

异步I / O会增加每个请求的内存使用量,但它会减少每个请求的线程使用率。 所以你最终获胜(除了临界病理角落病例)。 所有异步I / O都是如此,包括async

await设计了一种模式 - 不仅仅是Task类型 - 所以如果你需要尽可能多的表现,你可以。

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

Stephen Toub阅读的文章非常出色。 我也推荐Async视频的Zen(也由Stephen Toub提供)。

使用这些关键字的异步编程非常容易,但是与套接字的SocketAsyncEventArgs一样好?

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

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

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

我不知道莫诺。 在.NET上,大多数异步I / O方法都基于完成端口。 Stream类是一个明显的例外。 Stream基类默认会做一个“便宜的包装器”,但允许派生类覆盖这种行为。 来自网络通信的数据Stream总是覆盖这个来提供真正的异步I / O。 Stream与文件打交道的唯一如果流被明确构建异步I / O覆盖。

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

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

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

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

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

如果您为每个请求提供了单线程的SynchronizationContext例如AsyncContext ,那么即使它没有任何关系,它也会将线程绑定到每个请求。 这并不比同步多线程服务器更好。

如果你想解决发明你自己的SynchronizationContext你可以在SynchronizationContext上找到我的MSDN文章。 我还在那篇文章中介绍了异步方法如何“注册”和“安装”上下文; 这主要是通过async void自动完成的,并且await所以你不需要明确地做。


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

  • 这些方法不仅仅是阻塞线程池线程的封装,不是。 这会挫败await代表的目的。 这些方法不会阻塞任何线程,并依靠OS挂钩来完成任务。

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

  • 链接地址: http://www.djcxy.com/p/79313.html

    上一篇: async/await for high performance server applications?

    下一篇: Why does the async keyword exist