問: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.
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.app
回答1:less
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.async
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).ide
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:fetch
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.ui
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."this
回答2:spa
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"。
參考文獻: