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

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

问题描述

在这里Techdays在荷兰的史蒂夫·桑德森在给有关 <一presentation EM> C#5,ASP.NET MVC 4,和异步Web 的。

他解释说,当请求需要很长的时间才能完成,全部由线程池线程变得忙碌,新的请求必须等待。服务器无法处理负载,一切都慢下来。

他随后展示了使用异步webrequests如何提高性能,因为工作是委托,然后到另一个线程和线程池能够为新的传入请求迅速作出反应。他甚至演示了这一点,并表明,50个并发请求第一次带50 * 1秒,但在总共只有1,2 S IN的地方异步行为。

不过,看到这个后,我仍然有一些问题。


  1. 为什么我们不能只使用一个线程池更大?不使用异步/等待弹出另一个线程慢那么从一开始就只是增加线程池?它不喜欢的服务器,我们运行在突然得到更多的线程或东西吗?


  2. 来自用户的请求仍在等待异步线程完成。如果从池中的线程别的做的事情,怎么会是UI线程不停地忙碌着?史蒂夫提到的一些关于当事情结束后,知道一个聪明的内核。这是如何工作的?



解决方案

这是一个很好的问题,并了解它的关键是理解为什么异步IO是非常重要的。为什么新的异步/等待功能已被添加到C#5.0的原因是为了简化编写异步code。在服务器上的异步处理的支持是不是新的,但是由于ASP.NET 2.0它的存在。

像史蒂夫向您展示了,与同步处理,在ASP.NET(和WCF)每个请求需要从线程池中一个线程。他演示了该问题是所谓的一个众所周知的问题,线程池饥饿。如果您同步IO您的服务器上,线程池中的线程将保持阻塞(什么都不做)的IO的持续时间。由于在线程池中的线程的数目的限制,在负载下,这可能在一个情况导致,其中所有的线程池线程被阻塞等待IO,并请求开始被排队,从而导致增加了响应时间。由于所有的线程都在等待一个IO完成,你会看到一个CPU占用接近0%(即使响应时间,通过屋顶)。

你的要求(为什么我们不能只使用一个线程池更大?)是一个非常好的问题。作为事实上,这是何等的大部分人都在解决线程池饥饿的问题,到现在为止:只要有线程池的多个线程。从微软的一些文件甚至表示,作为一个的情况下修复时可能出现的线程池饥饿。这是一个可以接受的解决方案,直到C#5.0中,要做到这一点,不是重写你的code是完全异步的,要容易得多。

有与方式的几个问题,但:


  • 有没有价值,在所有情况下的工作原理:你会需要线程池中的线程数量线性依赖于IO的持续时间,和你的服务器上的负载。不幸的是,IO延迟主要是未predictable。下面是一个为例:
    比方说,你做的HTTP请求到第三方Web服务在ASP.NET应用程序,这需要大约2秒完成。您遇到线程池饥饿,所以你决定增加线程池大小,比方说,200个线程,然后再次开始工作的罚款。的问题是,也许下周,web服务将具有增加其应答时间为10秒的技术问题。所有的突然,线程池饥饿又回来了,因为线程被阻塞长5倍,所以你现在需要增加数的5倍,到1000线。


  • 可扩展性和性能:第二个问题是,如果你这样做,你仍然会使用每个请求一个线程。线程是一种昂贵的资源。在.NET中每个托管线程需要1 MB的堆栈内存分配。对于网页制作的IO持续5秒,每秒500个请求的负载,你需要在你的线程池中的线程2500,这意味着2.5 GB的内存,以便将坐在什么都不做的线程的堆栈。然后你有上下文切换的问题,这将需要付出昂贵的代价你的机器(影响机器上的所有服务,而不仅仅是你的Web应用程序)的性能。即使Windows在忽略等待的线程做了相当不错的工作,它不是设计来处理如此大量的线程。请记住,当正在运行的线程的数目等于逻辑CPU的机器上(通常不超过16个)的数量,获得最高的效率。


因此​​增加线程池的大小是一个解决方案,而人们也一直在这样做了十年(甚至在微软自己的产品),它仅仅是少可扩展性和高效率,在内存和CPU使用率方面,你总是在IO延迟的突然增加会导致饥饿的摆布。直到C#5.0,异步code的复杂性,是不值得的麻烦了很多人。异步/的await改变了一切和现在一样,你可以受益于异步IO的可扩展性,并编写一些简单code,在同一时间。

更多细节:<一href=\"http://msdn.microsoft.com/en-us/library/ff647787.aspx\">http://msdn.microsoft.com/en-us/library/ff647787.aspx 的使用异步调用来调用Web服务或远程对象时,有执行额外的并行处理,而Web服务调用的收益。如果可能的话,应避免同步(阻塞)的机会,因为传出Web服务调用由调用Web服务通过使用线程从ASP.NET线程池。阻塞调用减少可用线程数来处理其他传入的请求。

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.

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. Why can't we just use a bigger threadpool? Isn't using async/await to bring up another thread slower then just increasing the threadpool from the start? It's not like the server we run on suddenly get more threads or something?

  2. The request from the user is still waiting for the async thread to finish. If the thread from the pool is doing something else, how is the 'UI' thread kept busy? Steve mentioned something about 'a smart kernel that knows when something is finished'. How does this work?

解决方案

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.

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).

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:

  • 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.

  • 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).

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.

More details: http://msdn.microsoft.com/en-us/library/ff647787.aspx "Use asynchronous calls to invoke Web services or remote objects when there is an opportunity to perform additional parallel processing while the Web service call proceeds. Where possible, avoid synchronous (blocking) calls to Web services because outgoing Web service calls are made by using threads from the ASP.NET thread pool. Blocking calls reduce the number of available threads for processing other incoming requests."

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

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