2014-03-24 56 views
0

我目前正試圖弄清楚何時使用Task.Run,​​何時不使用。Asp.Net WebApi運行MongoDB查詢/保存異步

在我的項目中,我使用WebApi與MongoDB結合來存儲帳戶信息。

因此,對於下面的示例代碼,我會更好地使用來自客戶端調用的提交或SumbitAsync方法嗎?

public class TestController : ApiController 
{ 
    [HttpPost] 
    public void Submit() 
    { 
     DoSave(); 
    } 

    public async Task SubmitAsync() 
    { 
     await Task.Run(() => DoSave()); 
    } 

    private void DoSave() 
    { 
     myMongoDbCollection.Save(new TestEntity()); 
    } 
} 

MongoDB C#驅動程序目前不支持異步方法。

回答

1

在這裏使用Task.Run毫無意義。

在ASP.NET中使用異步I/O的目的是釋放你的線程,所以它可以處理其他請求,直到I/O完成。同時,線程將被阻塞。當I/O操作完成時,I/O完成端口將發出信號,您的方法將恢復。

使用Task.Run,你只是推遲這個線程池線程,並使線程塊等待I/O。

換句話說,如果客戶端不支持異步I/O,則其中一個線程將始終阻塞。所以你不妨同時做所有事情,避免不必要的上下文切換。

Stephen Cleary's blog post "Task.Run Etiquette Examples: Don't Use Task.Run in the Implementation"

有(至少)只要您使用ASP.NET與Task.Run等待推出四款效率問題:

  • 額外的(不必要的)螺紋切換到Task.Run線程池線程。同樣,當該線程完成請求時,它必須輸入請求上下文(這不是實際的線程切換,但確實有開銷)。

  • 額外(不必要的)垃圾被創建。異步編程是一個折衷方案:以更高的內存使用率爲代價獲得更高的響應速度。在這種情況下,您最終會爲完全不必要的異步操作創建更多垃圾。

  • ASP.NET線程池啓發式被Task.Run「意外」借用線程池線程拋出。我在這裏沒有很多經驗,但我的直覺告訴我,如果意外任務非常短,啓發式應該會恢復得很好,如果意外任務持續超過兩秒鐘,就不會如此優雅地處理它。

  • ASP.NET無法提前終止請求,即客戶端斷開連接或請求超時。在同步的情況下,ASP.NET知道請求線程並可以中止它。在異步情況下,ASP.NET不知道輔助線程池線程是「for」該請求。可以通過使用取消令牌來解決這個問題,但這不在本博文的範圍之內。