我正在嘗試改進ASP.NET應用程序的性能。我正在研究的一件事是使用並行性和異步操作來嘗試減少處理時間並提高吞吐量。我開始嘲笑我們經常做的事情,發出多個數據庫檢索來呈現頁面。多個並行等待任務
public ActionResult Index()
{
var dal = new Dal();
var cases = new List<Case>();
cases.AddRange(dal.GetAssignedCases());
cases.AddRange(dal.GetNewCases());
return View("Cases", cases);
}
兩個達爾方法使用Thread.Sleep(2000)
模擬查詢,就回到硬編碼對象的集合。我使用ab -c 1 -n 1
與Apache Bench一起運行,大約需要4秒。我第一次嘗試,試圖改善它是:
public ActionResult Index()
{
var dal = new Dal();
var assignedCases = Task.Factory.StartNew(() => dal.GetAssignedCases());
var newCases = Task.Factory.StartNew(() => dal.GetNewCases());
IEnumerable<Case>[] allCases = Task.WhenAll(assignedCases, newCases).Result;
return View("Cases", allCases.SelectMany(c => c));
}
當我運行這個使用相同的ab
命令它顯示大約兩秒鐘,這是有道理的,因爲我跑兩個任務的每一個需要2秒,但他們並行運行。
當我將基準更改爲10個併發請求時(即ab -n 10 -c 10
),我得到以下內容。
Fulfilled Original Parallel
50% 4014 2038
66% 4015 2039
75% 4017 4011
其餘數字高達100%在兩欄都是相似的。
我假設我在這裏遇到的是線程池爭用。大約2/3的請求很快就完成了,之後這些東西就在等待線程處理請求。所以我想也許如果我加入了異步的組合,我可以更快地提供更多的請求。這就是我開始遇到問題的原因,我不知道問題是我模擬長時間運行的查詢的方式,還是我使用語言功能的方式,或者如果我完全在錯誤的軌道上,在隧道的盡頭是一輛即將到來的火車。 :-)
我做的第一件事就是創建一個DalAsync。在DalAsync中,我將Thread.Sleep(2000)
替換爲await Task.Delay(2000)
,用async
關鍵字標記每種方法,並將返回類型從IEnumerable<Case>
更改爲Task<IEnumerable<Case>>
。然後,我編寫了一個新的控制器方法,根據我在六篇博客文章和MSDN文章中閱讀的信息拼湊起來。
public async Task<ActionResult> Index()
{
var dal = new DalAsync();
var assignedCases = dal.GetAssignedCasesAsync();
var newCases = dal.GetNewCasesAsync();
var allCases = await Task.WhenAll(assignedCases, newCases);
return View("Cases", allCases.SelectMany(c => c));
}
當我此使用ab
它從未完成,即使它最終超時一個請求運行。我也嘗試了下面的變體,它可以工作,但返回的數字幾乎與原始版本相同(這種說法有意義,因爲它似乎是我再次序列化查詢)。
var assignedCases = await dal.GetAssignedCasesAsync();
var newCases = await dal.GetNewCasesAsync();
var allCases = new List<Case>(assignedCases);
allCases.AddRange(newCases);
我想什麼有發生的情況是:
- 平行
- 運行兩個查詢當控制器正在等待達爾方法來應對它釋放了線程,並讓其他 請求執行。
您的'WhenAll'方法應該可以工作。確保你的目標是.NET 4.5,並且[通知ASP.NET你的目標是.NET 4.5](http://blogs.msdn.com/b/webdev/archive/2012/11/19/all-about-的httpRuntime-targetframework.aspx)。 –
重啓後,一切似乎都按預期開始工作。幾乎看起來像是在IIS中「卡住」了。 –