2017-02-22 114 views
1

我試圖向同事解釋爲什麼async void函數很糟糕,並且異常不會被捕獲,但事實證明我可能不會理解它們是正確的。我們有一段代碼,看起來有點像這樣:在發送結果之前,MVC函數返回但等待異步函數

public ActionResult EmailCandidates(List<string> identityTokens, string subject, string content) 
{ 
    // generate list of recipients here 
    SendEmail(recipients, subject, content); //not awaited 
    return new AjaxResponse { // AjaxResponse is a wrapper around JSONResponse 
     IsSuccess = true, 
     Data = recipients.Select(r=>r.Name) 
    }; 
} 

private async void SendEmail(List<EmailAddress> recipients, string subject, string content) 
{ 
    await Task.Delay(10000); // simulate async send email 
    throw new Exception(); // manually added 
} 

我所期待的,而我試圖解釋的是,如果SendEmail函數拋出一個異常,將無法正確抓到,因爲主功能EmailCandidates已經返回給客戶端。只是這不會發生。上面的代碼執行完全相同的順序我想到:

  • 一個呼叫從客戶機向EmailCandidates
  • SendEmail
  • 電子郵件是同步發送(通過異步等待在這裏模擬)
  • 控制返回到EmailCandidates,並返回執行

,然後它會有點奇怪:

  • 在這一點上,我希望得到客戶端的響應,但我不這樣做,即使EmailCandidates返回
  • 10秒後的異常被拋出
  • 異常是由全球陷入錯誤處理器,現在客戶端會收到一個500錯誤(不出所料)

那麼,爲什麼即使EmailCandidates又回來了,沒有得到發送到客戶端的響應。如何知道等待異步SendEmail函數?

+0

快速問題,你有什麼配置這是你的webconfig? Zinov

+0

@Zinov web配置非常大,但自定義錯誤是' – Anduril

+0

檢查波紋管我的回答 – Zinov

回答

3

ASP.NET提供了一個SynchronizationContext,用於跟蹤正在運行的異步操作的數量,並且在完成所有操作之前不會發送結果。請注意,此SynchronizationContext已在ASP.NET Core中刪除。

但是,即使在ASP.NET上也不應該看到這種行爲。在調用async void方法的同步方法的情況下,您應該看到InvalidOperationException帶有消息「此時無法啓動異步操作」。對於調用async void方法(在處理程序返回前未完成)的異步方法,應該會看到一條消息InvalidOperationException,其消息「異步模塊或處理程序在異步操作尚未完成時完成」。

既然這些安全網絡都沒有觸發,我懷疑你的ASP.NET代碼使用的是舊版本的ASP.NET。這些安全網是在.NET 4.5中添加的,您不僅必須將其作爲構建目標,而且還需要必須爲add targetFramework in your web.config

甲新的.NET 4.5.2 ASP.NET MVC應用用下面的代碼立即引發InvalidOperationException,如所預期:

public ActionResult About() 
{ 
    ViewBag.Message = "Your application description page."; 
    Test(); 
    return View(); 
} 

private async void Test() 
{ 
    await Task.Delay(20000); 
    throw new Exception("Blah"); 
} 
+0

我們使用舊版本的.NET。事實上,這個問題的出現是因爲我們在Web配置中設置了UseTaskFriendlySynchronizationContext標誌來與我們剛剛安裝的signalr配合使用。那時我們得到了你描述的錯誤。我沒有意識到MVC在返回之前等待所有異步函數。非常有趣 – Anduril

+0

@Anduril:'UseTaskFriendlySynchronizationContext' *必須*設置爲'true',否則'async' /'await'可能無法正常工作。 –

+0

目前我們不使用任何異步/等待代碼(除了這一個函數,我不知道甚至存在被埋在網站的一個小部分中),所以我們已經能夠逃脫遠。據推測,一旦我們將targetFramework設置爲4.5或更高版本,我們就不需要將UseTaskFriendlySynchronizationContext設置爲true。 – Anduril

0

異步空隙方法有點從「正常的」異步方法不同的東西。他們有不同的錯誤處理邏輯。當異常從異步任務或異步任務方法中拋出時,該異常被捕獲並放置在任務對象上。使用async void方法時,不存在任何Task對象,因此異步void方法拋出的異常將在調用async void方法時處於活動狀態的SynchronizationContext上直接引發。使用UnhandledException事件處理程序可以觀察到這些異常。

0

您的應用程序工作正常,實際上是遵循MVC的默認行爲。如果明確指出異常,那麼當請求源自應用程序所在的同一機器(本地主機)時,如果您想查看實際用戶想要查看的信息,則需要更改此類錯誤(500)默認情況下它被設置爲RemoteOnly。

如果你去你的FilterConfig。你會看到這行代碼

public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
     { 
      filters.Add(new HandleErrorAttribute()); 
     } 

試圖改變價值「開」,你會看到這樣的錯誤頁面上結束,因爲處理程序錯誤屬性,它是一個行動提供後期處理邏輯和當它看到一個異常已經從一個動作中逃脫時,它將顯示一個錯誤視圖,而不是黃色的死亡屏幕。錯誤視圖是由內查看/共享/ Error.cshtml

你關選項

默認應用程序指定自定義錯誤被禁用。這允許顯示 詳細錯誤。

參考去這裏:https://msdn.microsoft.com/en-us/library/h0hfz6fc(v=vs.71).aspx

如果你把遙控器只有這樣,你會繼續看到的錯誤,如果你正在調試你的LOCALMACHINE的網站,但如果你的主機應用程序的最終用戶將看不到那個錯誤。