为什么使用异步请求而不是使用更大的线程池? [英] Why use async requests instead of using a larger threadpool?

查看:39
本文介绍了为什么使用异步请求而不是使用更大的线程池?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在荷兰的 Techdays 期间,Steve Sanderson 发表了关于 C 的演讲#5、ASP.NET MVC 4 和异步 Web.

During the Techdays here in the Netherlands Steve Sanderson gave a presentation about C#5, ASP.NET MVC 4, and asynchronous Web.

他解释说,当请求需要很长时间才能完成时,线程池中的所有线程都会变得忙碌,新的请求必须等待.服务器无法处理负载,一切都变慢了.

He explained that when requests take a long time to finish, all the threads from the threadpool become busy and new requests have to wait. The server can't handle the load and everything slows down.

然后他展示了异步 webrequest 的使用如何提高性能,因为工作随后被委托给另一个线程,线程池可以快速响应新的传入请求.他甚至对此进行了演示,并表明 50 个并发请求首先需要 50 * 1 秒,但在异步行为到位的情况下,总共只需要 1.2 秒.

He then showed how the use of async webrequests improves performance because the work is then delegated to another thread and the threadpool can respond quickly to new incoming requests. He even demoed this and showed that 50 concurrent requests first took 50 * 1s but with the async behavior in place only 1,2 s in total.

但是看到这个之后我还是有一些疑问.

But after seeing this I still have some questions.

  1. 为什么我们不能使用更大的线程池?是不是使用 async/await 来启动另一个线程的速度比从一开始就增加线程池的速度要慢?是不是我们运行的服务器突然多了线程什么的?

来自用户的请求仍在等待异步线程完成.如果池中的线程正在做其他事情,UI"线程如何保持忙碌?史蒂夫提到了一些关于知道某事何时完成的智能内核"的内容.这是如何工作的?

推荐答案

这是一个很好的问题,理解它是理解为什么异步 IO 如此重要的关键.在 C# 5.0 中添加新的 async/await 功能的原因是为了简化异步代码的编写.在服务器上支持异步处理并不是什么新鲜事,它从 ASP.NET 2.0 开始就存在了.

This is a very good question, and understanding it is key to understand why asynchronous IO is so important. The reason why the new async/await feature has been added to C# 5.0 is to simplify writing asynchronous code. Support for asynchronous processing on the server is not new however, it exists since ASP.NET 2.0.

就像史蒂夫向您展示的那样,通过同步处理,ASP.NET(和 WCF)中的每个请求都从线程池中获取一个线程.他演示的问题是一个众所周知的问题,称为线程池饥饿".如果您在服务器上进行同步 IO,则线程池线程将在 IO 期间保持阻塞状态(什么都不做).由于线程池中的线程数是有限制的,在负载下,这可能会导致所有线程池线程都被阻塞等待IO,请求开始排队,导致响应时间增加.由于所有线程都在等待 IO 完成,您将看到 CPU 占用率接近 0%(即使响应时间过长).

Like Steve showed you, with synchronous processing, each request in ASP.NET (and WCF) takes one thread from the thread pool. The issue he demoed is a well known issue called "thread pool starvation". If you make synchronous IO on your server, the thread pool thread will remain blocked (doing nothing) for the duration of the IO. Since there is a limit in the number of threads in the thread pool, under load, this may lead in a situation where all the threads pool threads are being blocked waiting for IO, and requests starts being queued, causing an increase to response time. Since all the threads are waiting for an IO to complete, you will see a CPU occupation close to 0% (even though response times go through the roof).

您要问的是(为什么我们不能只使用更大的线程池?)是一个很好的问题.事实上,到目前为止,大多数人都是这样解决线程池饥饿问题的:线程池上有更多线程.来自 Microsoft 的一些文档甚至指出,作为对可能发生线程池饥饿的情况的修复.这是一个可以接受的解决方案,在 C# 5.0 之前,这样做比重写代码以完全异步要容易得多.

What you are asking (Why can't we just use a bigger threadpool?) is a very good question. As a matter of fact, this is how most people have been solving the problem of thread pool starvation until now: just have more threads on the thread pool. Some documentation from Microsoft even indicates that as a fix for situations when thread pool starvation may occur. This is an acceptable solution, and until C# 5.0, it was much easier to do that, than rewriting your code to be fully asynchronous.

尽管这种方法存在一些问题:

There are a few problems with the approach though:

  • 没有适用于所有情况的价值:您将需要的线程池线程数与 IO 的持续时间和服务器上的负载呈线性关系.不幸的是,IO 延迟大多是不可预测的.这是一个例子:假设您向 ASP.NET 应用程序中的第三方 Web 服务发出 HTTP 请求,该请求大约需要 2 秒才能完成.您遇到线程池不足的问题,因此您决定将线程池大小增加到 200 个线程,然后它再次开始正常工作.问题是,也许下周,Web 服务会出现技术问题,将响应时间增加到 10 秒.突然之间,线程池饥饿又回来了,因为线程被阻塞的时间长了 5 倍,所以您现在需要将数量增加 5 倍,达到 1,000 个线程.

  • There is no value that works in all situations: the number of thread pool threads you are going to need depends linearly on the duration of the IO, and the load on your server. Unfortunately, IO latency is mostly unpredictable. Here is an exemple: Let's say you make HTTP requests to a third party web service in your ASP.NET application, which take about 2 seconds to complete. You encounter thread pool starvation, so you decide to increase the thread pool size to, let's say, 200 threads, and then it starts working fine again. The problem is that maybe next week, the web service will have technical problems which increases their response time to 10 seconds. All of the sudden, thread pool starvation is back, because threads are blocked 5 times longer, so you now need to increase the number 5 times, to 1,000 threads.

可扩展性和性能:第二个问题是,如果这样做,每个请求仍将使用一个线程.线程是一种昂贵的资源..NET 中的每个托管线程都需要为堆栈分配 1 MB 的内存.对于一个持续 5 秒的 IO 和每秒 500 个请求的负载的网页,您的线程池中将需要 2,500 个线程,这意味着 2.5 GB 的内存用于线程堆栈,这些线程将无所事事.然后你会遇到上下文切换的问题,这会对你的机器的性能造成很大的影响(影响机器上的所有服务,而不仅仅是你的 web 应用程序).尽管 Windows 在忽略等待线程方面做得相当不错,但它并不是为处理如此大量的线程而设计的.请记住,当运行的线程数等于机器上的逻辑 CPU 数(通常不超过 16 个)时,效率最高.

Scalability and performance: The second problem is that if you do that, you will still use one thread per request. Threads are an expensive resource. Each managed thread in .NET requires a memory allocation of 1 MB for the stack. For a webpage making IO that last 5 seconds, and with a load of 500 requests per second, you will need 2,500 threads in your thread pool, that means 2.5 GB of memory for the stacks of threads that will sit doing nothing. Then you have the issue of context switching, that will take a heavy toll on the performance of your machine (affecting all the services on the machine, not just your web application). Even though Windows does a fairly good job at ignoring waiting threads, it is not designed to handle such a large number of threads. Remember that the highest efficiency is obtained when the number of threads running equals the number of logical CPUs on the machine (usually not more than 16).

所以增加线程池的大小是一个解决方案,人们已经这样做了十年(即使在微软自己的产品中),它只是在内存和 CPU 使用方面的可扩展性和效率较低,而你总是受到会导致饥饿的 IO 延迟突然增加的摆布.在 C# 5.0 之前,异步代码的复杂性对于很多人来说并不值得.async/await 改变了现在的一切,您可以从异步 IO 的可扩展性中受益,同时编写简单的代码.

So increasing the size of the thread pool is a solution, and people have been doing that for a decade (even in Microsoft's own products), it is just less scalable and efficient, in terms of memory and CPU usage, and you are always at the mercy of a sudden increase of IO latency that would cause starvation. Up until C# 5.0, the complexity of asynchronous code wasn't worth the trouble for many people. async/await changes everything as now, you can benefit from the scalability of asynchronous IO, and write simple code, at the same time.

更多详情:http://msdn.microsoft.com/en-us/library/ff647787.aspx "如果在 Web 服务调用进行期间有机会执行额外的并行处理,请使用异步调用来调用 Web 服务或远程对象.在可能的情况下,避免同步(阻塞)调用到 Web 服务,因为传出 Web 服务调用是使用来自 ASP.NET 线程池的线程进行的.阻塞调用会减少处理其他传入请求的可用线程数."

这篇关于为什么使用异步请求而不是使用更大的线程池?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆