简体   繁体   English

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

[英]async/await for high performance server applications?

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

Async-programming using these keywords is so much easier but is it as good as say SocketAsyncEventArgs for Sockets ? 使用这些关键字进行异步编程要容易得多,但就像套接字的SocketAsyncEventArgs一样好吗?

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

Third question: Beside the SynchronizationContext of an UI application, is there a way to implement some kind of sinlge-threaded context ? 第三个问题:在UI应用程序的SynchronizationContext旁边,有没有办法实现某种sinlge-threaded上下文? 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. 我发现了Nito.AsyncEx库,但我不确定这是否是我需要的。

async itself is quite performant. async本身非常高效。 A ton of work went into this. 大量的工作进入了这个。

In general, on the server side you're concerned about async I/O. 通常,在服务器端,您会关注async I / O. I'm going to ignore async CPU-bound methods because the async overhead will get lost in the noise anyway. 我将忽略async CPU绑定方法,因为无论如何async开销都会在噪声中丢失。

Asynchronous I/O will increase your memory usage per request, but it'll reduce your thread usage per request. 异步I / O将增加每个请求的内存使用量,但它会减少每个请求的线程使用量。 So you end up winning (except borderline pathological corner cases). 所以你最终获胜(边缘病态角落病例除外)。 This is true for all asynchronous I/O, including async . 这适用于所有异步I / O,包括async

await was designed with a pattern - not just the Task type - so if you need to squeeze out as much performance as possible, you can. await设计有一个模式 - 而不仅仅是Task类型 - 所以如果你需要尽可能多地挤出性能,你可以。

I read an article about the performance impact on those applications since the compiler will generate a quite complex state machine for async methods. 我读了一篇关于性能对这些应用程序影响的文章,因为编译器会为异步方法生成一个非常复杂的状态机。

The article you read by Stephen Toub is excellent. 你读过 Stephen Toub的文章很棒。 I also recommend the Zen of Async video (also by Stephen Toub). 我还推荐Zen of Async视频 (也由Stephen Toub提供)。

Async-programming using these keywords is so much easier but is it as good as say SocketAsyncEventArgs for Sockets ? 使用这些关键字进行异步编程要容易得多,但就像套接字的SocketAsyncEventArgs一样好吗?

First, understand that SocketAsyncEventArgs is more scalable because it reduces memory garbage. 首先,要了解SocketAsyncEventArgs的可伸缩性更高,因为它可以减少内存垃圾。 The simpler way to use async sockets will generate more memory garbage, but since await is pattern-based you can define your own async -compatible wrappers for the SocketAsyncEventArgs API (as seen on Stephen Toub's blog... I'm sensing a pattern here ;). 使用async套接字的简单方法会产生更多内存垃圾,但由于await是基于模式的,因此您可以SocketAsyncEventArgs API定义自己的async兼容包装器 (如Stephen Toub的博客所示......我在这里感知一种模式)。 This allows you to squeeze every ounce of performance out. 这可以让你挤出每一盎司的性能。

Though it's usually better in the long run to design a scale-out system rather than twisting the code to avoid a few memory allocations. 虽然从长远来看,设计一个横向扩展系统通常更好,而不是扭曲代码以避免一些内存分配。 IMHO. 恕我直言。

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

I don't know about Mono. 我不知道Mono。 On .NET, most asynchronous I/O methods are based on a completion port. 在.NET上, 大多数异步I / O方法都基于完成端口。 The Stream class is a notable exception. Stream类是一个值得注意的例外。 The Stream base class will do a "cheap wrapper" by default, but allows derived classes to override this behavior. 默认情况下, Stream基类将执行“廉价包装”,但允许派生类覆盖此行为。 Stream s that come from network communications always override this to provide truly asynchronous I/O. 来自网络通信的Stream总是覆盖它以提供真正的异步I / O. Stream s that deal with files only override this if the stream was constructed explicitly for asynchronous I/O. Stream与文件打交道的唯一如果流被明确构建异步I / O覆盖。

Third question: Beside the SynchronizationContext of an UI application, is there a way to implement some kind of single-threaded context ? 第三个问题:在UI应用程序的SynchronizationContext旁边,有没有办法实现某种单线程上下文?

ASP.NET also has a SynchronizationContext , so if you're using ASP.NET you're already set. ASP.NET也有一个SynchronizationContext ,所以如果你使用的是ASP.NET,那么你已经设置好了。

If you are doing your own socket-based server (eg, a Win32 service), then you could use the AsyncContext type in my AsyncEx library. 如果您正在使用自己的基于套接字的服务器(例如,Win32服务),那么您可以在我的AsyncEx库中使用AsyncContext类型。 But it doesn't sound like this is what you'd actually want. 但这听起来并不像你真正想要的那样。 AsyncContext will create a single-threaded context on the current thread. AsyncContext将在当前线程上创建单线程上下文。 But the true power of async for server applications comes from scaling requests instead of threads . 但是,服务器应用程序async的真正威力来自扩展请求而不是线程

Consider how the ASP.NET SynchronizationContext works: as each request comes in, it grabs a thread pool thread and constructs a SynchronizationContext (for that request ). 考虑ASP.NET SynchronizationContext工作原理:当每个请求进入时,它会抓取一个线程池线程并构造一个SynchronizationContext (用于该请求 )。 When that request has asynchronous work to do, it registers with the SynchronizationContext and the thread running that request returns to the thread pool. 当该请求具有异步工作时,它将向SynchronizationContext注册,并且运行该请求的线程将返回到线程池。 Later, when the asynchronous work completes, it grabs a thread pool thread ( any thread), installs the existing SynchronizationContext on it, and continues processing that request. 稍后,当异步工作完成时,它会抓取线程池线程( 任何线程),在其上安装现有的SynchronizationContext ,并继续处理该请求。 When the request is finally completed, its SynchronizationContext is disposed. 最终完成请求后,将SynchronizationContextSynchronizationContext

The key in that process is that when the request is waiting ( await ) asynchronous operations, there are no threads dedicated to that request. 该过程的关键是当请求等待( await )异步操作时, 没有专用于该请求的线程。 Since a request is considerably lightweight compared to a thread , this enables the server to scale better. 由于与线程相比, 请求相当轻量级,因此服务器可以更好地扩展。

If you gave each of your requests a single-threaded SynchronizationContext such as AsyncContext , this would bind a thread to each request even when it has nothing to do. 如果您为每个请求提供单线程SynchronizationContext (如AsyncContext ,则会将线程绑定到每个请求,即使它没有任何操作。 That's hardly any better than a synchronous multithreaded server. 这几乎没有比同步多线程服务器更好的了。

You may find my MSDN article on SynchronizationContext useful if you want to tackle inventing your own SynchronizationContext . 如果您想要发明自己的SynchronizationContext可能会发现我SynchronizationContext上的MSDN文章非常有用。 I also cover in that article how asynchronous methods "register" and "install" the context; 我还在那篇文章中介绍了异步方法如何“注册”和“安装”上下文; this is done mostly-automatically by async void and await so you won't have to do it explicitly. 这主要由async voidawait自动完成,因此您不必明确地执行此操作。

  1. If you're using it in the context of async IO it's a moot point. 如果您在异步IO的上下文中使用它,那么这是一个有争议的问题。 The time spent on your database operation, file/network IO, etc. will be milliseconds at best. 花在数据库操作,文件/网络IO等上的时间最多为毫秒。 The overhead of async will be microseconds at worst, if no nanoseconds. 如果没有纳秒, async的开销在最坏的情况下将是微秒。 Where you need to be careful is when you have a lot of operations that you are awaiting (as in thousands, tens of thousands, or more) and those operations are very quick. 需要注意的地方是,当你有很多正在等待的操作时(如数千,数万或更多),这些操作非常快。 When those async operations represent CPU bound work it's certainly possible for the overhead of using await to be at least noticeable. 当这些异步操作代表CPU绑定工作时,使用await的开销肯定可能至少是显而易见的。 Note that the code generated for the state machine is somewhat complex, in terms of human understand-ability, but state machines in general tend to perform rather well as a whole. 请注意,就人类的理解能力而言,为状态机生成的代码有些复杂,但状态机通常总体上表现得相当好。

  2. The methods are not just wrappers that block a thread pool thread, no. 这些方法不仅仅是阻塞线程池线程的包装器,不是。 That would defeat the purpose of what await represents. 这将破坏await代表的目的。 Those methods don't block any thread and rely on OS hooks to complete the task. 这些方法不会阻塞任何线程并依赖OS挂钩来完成任务。

  3. Sure, you can create your own SynchronizationContext instead of relying entirely on an existing one provided by your UI framework. 当然,您可以创建自己的SynchronizationContext而不是完全依赖UI框架提供的现有版本。 Here is a great example. 是一个很好的例子。 Just be careful when using something like this; 使用这样的东西时要小心; it's a good tool for the right task, but can be abused to find a more creating way of blocking when you should just be doing everything asynchronously. 它对于正确的任务来说是一个很好的工具,但是当你应该异步完成所有事情时,可能会被滥用来找到更具创造性的阻塞方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM