首页 文章

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

提问于
浏览
14

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

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

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

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

2 回答

  • 6

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

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

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

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

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

    Stephen Toub的article you read非常出色 . 我也推荐Zen of Async video(也是Stephen Toub) .

    使用这些关键字的异步编程是如此简单,但是它与套接字的SocketAsyncEventArgs一样好吗?

    首先,要了解 SocketAsyncEventArgs 更具可扩展性,因为它可以减少内存垃圾 . 使用 async 套接字的简单方法会产生更多的内存垃圾,但由于 await 是基于模式的,你可以define your own async-compatible wrappers for the SocketAsyncEventArgs API(如Stephen Toub所见's blog... I'm感知这里的模式;) . 这可以让你挤出每一盎司的性能 .

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

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

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

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

    ASP.NET也有 SynchronizationContext ,所以如果你已经设置了're using ASP.NET you' .

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

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

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

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

    如果你想要发明自己的 SynchronizationContext ,你可能会发现我的MSDN article on SynchronizationContext非常有用 . 我还在那篇文章中介绍了异步方法"register"和"install"的上下文;这主要由 async voidawait 自动完成,因此您不必明确地执行此操作 .

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

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

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

相关问题