2013-07-18 151 views
0

我正在嘗試改進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); 

我想什麼有發生的情況是:

  • 平行
  • 運行兩個查詢當控制器正在等待達爾方法來應對它釋放了線程,並讓其他 請求執行。
+2

您的'WhenAll'方法應該可以工作。確保你的目標是.NET 4.5,並且[通知ASP.NET你的目標是.NET 4.5](http://blogs.msdn.com/b/webdev/archive/2012/11/19/all-about-的httpRuntime-targetframework.aspx)。 –

+0

重啓後,一切似乎都按預期開始工作。幾乎看起來像是在IIS中「卡住」了。 –

回答

0

你的第一個代碼示例應該可以工作,但是對於我的眼睛來說,它有點奇怪。引入了Task.WhenAll作爲非阻塞操作,即您將使用await Task.WhenAll(myTasks)。通過使用.Result,您可以將其轉變爲阻止操作,但以這種方式使用並不總是自然的。

我覺得你真的以後是Task.WaitAll(params Task[])它被設計成一個阻塞操作。

然而,你的第二個代碼示例看起來接近完美,並且我會去做什麼。在整個代碼庫中實現異步代碼總是使得實現更簡潔。

0

雖然我無法複製你的情況下,我的運行良好,但對我來說,似乎你正陷入僵局。嘗試強制異步任務將其結果返回到不同的同步上下文,如下所示:

public async Task<ActionResult> Index() 
{ 
    var dal = new DalAsync(); 
    var assignedCases = Task.Run(async() => await dal.GetAssignedCasesAsync()); 
    var newCases = Task.Run(async() => await dal.GetNewCasesAsync()); 
    var allCases = await Task.WhenAll(assignedCases, newCases).ConfigureAwait(false); 
    return View("Cases", allCases.SelectMany(c => c)); 
}