14

the new async/await keywords in C# 5 look very promising but I read an article about the performance impact on those applications since the compiler will generate a quite complex state machine for async methods.

Async-programming using these keywords is so much easier but is it as good as say SocketAsyncEventArgs for Sockets ?

Second question: Are asynchronous IO methods like Stream.WriteAsync really asynchronous (Completion Ports on .Net or epoll/poll on Mono) or are these methods cheap wrappers for pushing a write call to a threadpool ?

Third question: Beside the SynchronizationContext of an UI application, is there a way to implement some kind of sinlge-threaded context ? Something like an event loop so that finished tasks continues on the main thread ? I discovered the Nito.AsyncEx library, but I'm not quite sure whether or not this is what i need.

4

2 回答 2

32

async本身性能相当不错。为此投入了大量的工作。

一般来说,在服务器端,您关心的是asyncI/O。我将忽略async受 CPU 限制的方法,因为async无论如何开销都会在噪音中消失。

异步 I/O 会增加每个请求的内存使用量,但会减少每个请求的线程使用量。所以你最终会赢(边缘病态的极端案例除外)。这适用于所有异步 I/O,包括async.

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

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

您阅读的 Stephen Toub的文章非常棒。我还推荐Zen of Async 视频(也是 Stephen Toub 的)。

使用这些关键字进行异步编程要容易得多,但它和 SocketAsyncEventArgs for Sockets 一样好吗?

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

尽管从长远来看,设计横向扩展系统通常比扭曲代码以避免一些内存分配更好。恕我直言。

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

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

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

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

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

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

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

如果您为每个请求提供了一个单线程SynchronizationContext,例如AsyncContext,这会将一个线程绑定到每个请求,即使它无事可做。这几乎不比同步多线程服务器好。

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

于 2013-02-07T00:33:39.650 回答
6
  1. 如果您在异步 IO 的上下文中使用它,那将是一个有争议的问题。花费在数据库操作、文件/网络 IO 等上的时间最多只有几毫秒。如果没有纳秒,那么最坏的开销async将是微秒。您需要小心的地方是当您有大量等待操作(如数千、数万或更多)并且这些操作非常快时。当这些异步操作代表 CPU 密集型工作时,使用的开销当然可能await至少是显而易见的。请注意,就人类的理解能力而言,为状态机生成的代码有些复杂,但状态机总体上往往表现得相当好。

  2. 这些方法不仅仅是阻塞线程池线程的包装器,不。那将破坏await代表的目的。这些方法不会阻塞任何线程,而是依靠操作系统挂钩来完成任务。

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

于 2013-02-06T22:16:57.510 回答