Why use async requests instead of using a larger threadpool?(转载)
问:
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.
- 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?
- 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?
回答1:
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: https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff647787(v=pandp.10) "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."
回答2:
- Async/await is not based on threads; it is based on asynchronous processing. When you do an asynchronous wait in ASP.NET, the request thread is returned to the thread pool, so there are no threads servicing that request until the async operation completes. Since request overhead is lower than thread overhead, this means async/await can scale better than the thread pool.
- The request has a count of outstanding asynchronous operations. This count is managed by the ASP.NET implementation of SynchronizationContext. You can read more about SynchronizationContext in my MSDN article - it covers how ASP.NET's SynchronizationContext works and how await uses SynchronizationContext.
ASP.NET asynchronous processing was possible before async/await - you could use async pages, and use EAP components such as WebClient (Event-based Asynchronous Programming is a style of asynchronous programming based on SynchronizationContext). Async/await also uses SynchronizationContext, but has a much easier syntax.
--------------------------------------------------
@Wouter Asynchronous processing doesn't require threads. In ASP.NET, if you await an operation that isn't complete, then the await will schedule the remainder of the method as a continuation, and return. The thread is returned to the thread pool, leaving no threads servicing the request. Later, when the await operation completes, it will take a thread from the thread pool and continue servicing the request on that thread. So, asynchronous programming doesn't depend on threads. Though it does work well with threads if you need it: you can await a thread pool operation using Task.Run. – Stephen Cleary
--------------------------------------------------
@StephenCleary I think the main problem people have is this: "The thread is returned to the thread pool, leaving no threads servicing the request. Later, when the await operation completes,..." how does the await operation complete if no thread is used to handle the request? What executes that code? It doesn't complete 'spontaneously', something must run it. That's the vague part. – Frans Bouma
--------------------------------------------------
@FransBouma: This troubled me too when I first encountered the term "asynchronous IO" (while studying Node.js). After some research, I found that some operations can be performed asynchronously at hardware level by some devices, like the HD. The OS requests a read operation to the HD, and goes back to doing other stuff. The HD, by itself, will fetch the data, fill its (phisical) buffers and then dispatch a signal to the processor, indicating that the read is done. The OS detects this and then grabs some thread from a pool to continue the processing with the fetched data. – Raphael
从上面最后– Raphael的回答中,我们可以看到有些很耗时的操作(例如IO操作等),的确是不需要用.NET线程(thread)去执行的,因为这些操作可以由硬件去自动完成,完成后硬件层面会通知.NET程序使用线程再继续执行后续的操作。所以我们不必用.NET线程去一直等待这些耗时的操作完成,而应该用async/await模式让线程去做其它的事情,让.NET程序尽可能少地去申请和创建新的线程。
读完这篇文章后,我才明白为什么要在.NET中尽量用async/await模式,为什么async/await模式会提升.NET程序的性能,最关键的问题是async/await模式可以减轻"thread pool starvation"。
参考文献:
Why use async requests instead of using a larger threadpool?(转载)的更多相关文章
- [Reactive Programming] Async requests and responses in RxJS
We will learn how to perform network requests to a backend using RxJS Observables. A example of basi ...
- [Angular] Bind async requests in your Angular template with the async pipe and the "as" keyword
Angular allows us to conveniently use the async pipe to automatically register to RxJS observables a ...
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿![转载]
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- (转)Making 1 million requests with python-aiohttp
转自:https://pawelmhm.github.io/asyncio/python/aiohttp/2016/04/22/asyncio-aiohttp.html Making 1 millio ...
- C#多线程和异步(二)——Task和async/await详解(转载)
一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...
- 掌握 Async/Await
摘要: 还不用Async/Await就OUT了.. 原文:掌握 Async/Await 作者:Jartto Fundebug经授权转载,版权归原作者所有. 前端工程师肯定都经历过 JS 回调链狱的痛苦 ...
- pushlet
自己准备做一个小游戏,租个云服务,然后挂在网上,可以跟同学一起玩,不过首先布置的是,这个游戏是否能实现,多人在线网页游戏,考虑到是否能够实时查询,在网上借鉴了下聊天原理,http长连接,搜索到push ...
- 浏览器XMLHttpRequest案例
/* Cross-Browser XMLHttpRequest v1.2 ================================= Emulate Gecko 'XMLHttpRequest ...
- 快速稳定的维护PHP
Just to recap, previously we'd have this sort of thing: namespace me\adamcameron\testApp; use Guzzle ...
随机推荐
- java基础篇1之可变参数,增强for循环,自动装箱
1.java中可变参数应用 例如 add(int x,int... args) 1)只能放在参数列表的最后面 2)...位于变量类型和变量名之间,前后有无空格都可以 3)调用可变参数的方法时,编译器为 ...
- 搭建k8s集群的手顺
https://www.cnblogs.com/netsa/category/1137187.html
- T4模板编辑器
一 二.工具 (T4模板编辑器) 使用效果 1.tangibleT4EditorPlusModellingToolsVS2013.msi 2.devart T4 Editor for Visual ...
- 做dg时遇到的log_archive_dest、log_archive_dest_1、db_recovery_file_dest之间互相影响
前提:归档开启.默认不指定归档文件夹. 今晚遇到客户那里设置了闪回区和log_archive_dest.不停库做DG时,无法指定log_archive_dest_n參数,巨坑. .实验了下.结论例如以 ...
- 内网ip打洞-----p2p实现原理
网上找了非常多.代码大堆,原理讲清楚透彻的不多. 本人找几篇讲得好的来整理一下. 一片技术文章,最基本的讲清楚原理.假设再有完整的能执行的源码也可,关键是要把核心部分代码分析清楚. (1)问题的由来: ...
- [Golang] 从零開始写Socket Server(2): 自己定义通讯协议
在上一章我们做出来一个最基础的demo后,已经能够初步实现Server和Client之间的信息交流了~ 这一章我会介绍一下怎么在Server和Client之间实现一个简单的通讯协议.从而增强整个信息交 ...
- linux下内存
MMU由一个或一组芯片组成.其功能是把逻辑地址映射为物理地址,进行地址转换(MMU是CPU的一部分) 机器指令仍然用逻辑地址指定一个操作数的地址或一条指令的地址 每个逻辑地址都由一个段选择符(16位) ...
- dedecms织梦后台password忘记了怎么办?dedecms织梦后台password忘记怎样找回password?
方法一:自己用解密的方式 用phpmyadmin登陆后台数据库,查看 找到password:去除前三位和后一位,然后拷贝到http://www.cmd5.com/在线解密工具里面解密 watermar ...
- SVN客户端忽略无关文件
修改前请先备份文件 ~/.subversion/config. 1,打开Terminal,输入命令: $ open ~/.subversion/config 2,在打开的文件中寻找:`global ...
- C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志
C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...