這是一個很好的問題,理解它是瞭解異步IO如此重要的關鍵。新的異步/等待功能添加到C#5.0的原因是爲了簡化編寫異步代碼。但是,支持服務器上的異步處理並不新鮮,它自ASP.NET 2.0以來就存在。
就像史蒂夫告訴你的,通過同步處理,ASP.NET(和WCF)中的每個請求都從線程池獲取一個線程。他演示的問題是一個衆所周知的問題,稱爲「線程池飢餓」。如果您在服務器上創建同步IO,則線程池線程將在IO持續時間內保持阻塞狀態(無需執行任何操作)。由於線程池中的線程數量有限制,所以在負載下,這可能會導致所有線程池線程都被阻塞等待IO的情況,並且請求開始排隊,導致響應時間增加。由於所有線程都在等待IO完成,所以您將看到CPU佔用率接近0%(即使響應時間已經過去了)。
你在問什麼(爲什麼我們不能只使用一個更大的線程池?)是一個很好的問題。事實上,這是大多數人直到現在一直在解決線程池飢餓問題的方式:線程池中只有更多的線程。微軟的一些文檔甚至指出,修復線程池可能發生飢餓的情況。這是一個可以接受的解決方案,在C#5.0之前,要比重寫代碼完全異步更容易。
沒有與方法的幾個問題,但:
有沒有價值,在所有情況下的工作原理:您將需要線程池中的線程數量線性依賴於持續時間IO和服務器上的負載。不幸的是,IO延遲大多是不可預測的。這裏是一個例子: 假設你在ASP.NET應用程序中向第三方Web服務發出HTTP請求,這需要大約2秒的時間才能完成。你會遇到線程池餓死的情況,所以你決定增加線程池的大小,比如說200個線程,然後它再次開始工作。問題是,也許下週,網絡服務將出現技術問題,將響應時間提高到10秒。突然之間,線程池飢餓又回來了,因爲線程被阻塞了5倍,所以你現在需要增加5次,到1,000線程。
可擴展性和性能:第二個問題是,如果你這樣做,你仍然會爲每個請求使用一個線程。線程是一個昂貴的資源。 .NET中的每個託管線程都需要爲該堆棧分配1 MB的內存。對於持續5秒製作IO的網頁,每秒加載500次請求的情況下,您的線程池中需要2,500個線程,這意味着線程堆棧的內存爲2.5 GB,無需任何操作。然後你就會遇到上下文切換的問題,這會嚴重影響你的機器的性能(影響機器上的所有服務,而不僅僅是你的Web應用程序)。儘管Windows在忽略等待線程方面做得相當不錯,但它並不是用來處理如此大量的線程。請記住,當運行的線程數量等於機器上的邏輯CPU數量(通常不超過16)時,可以獲得最高的效率。
因此增加線程池的大小是一個解決方案,而人們也一直在這樣做了十年(甚至在微軟自己的產品),它僅僅是少可擴展性和高效率,在內存和CPU方面使用情況,而且你總是處於可能導致飢餓的IO延遲突然增加的情況下。直到C#5.0之前,異步代碼的複雜性對許多人來說都是不值得的。 async/await會像現在一樣改變一切,您可以從異步IO的可伸縮性中受益,並且可以同時編寫簡單的代碼。
更多細節:http://msdn.microsoft.com/en-us/library/ff647787.aspx「使用異步調用來調用Web服務或遠程對象時,有執行額外的並行處理,而Web服務調用進行可能的話,應避免同步(阻塞)的機會調用Web服務,因爲傳出的Web服務調用是通過使用ASP.NET線程池中的線程來完成的。阻塞調用減少了處理其他傳入請求的可用線程數。「
此回覆不回答問題的第二部分。 – nunespascal 2012-06-12 04:23:36
爲什麼轉向異步模式的理由很合理。 – eduncan911 2012-11-03 00:47:48
我不認爲這解決了這樣一個事實,即不管I/O是不可預測的,無論其他規定如何,用戶在獲得響應之前都必須等待一切完成。 http/web服務器本身可以處理更多負載的事實並不意味着它能夠完全處理請求。我沒有看到async如何解決這個問題,除了改變事物的分佈方式以及可能引入更昂貴的上下文切換。使用asyc api的 – nilskp 2012-11-19 20:50:30