您好,登錄后才能下訂單哦!
小編給大家分享一下ASP.NET如何使用async/await,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
異常捕獲
async 方法有三種返回類型: void、Task、Task
async void
該方式聲明的方法是無法使用 catch 捕獲異常的,所以以下代碼的 try、catch 并沒什么卵用。
private static async void ThrowExceptionAsync() { await Task.Delay(1000); throw new Exception("拋個異常玩玩"); } public static async void CatchAsyncVoidException() { try { ThrowExceptionAsync(); } catch (Exception ex) { throw ex; } }
async Task 或 async Task
這兩種方式聲明的方法異常信息會包含Task屬性內,但前提需要在try里面使用 await 等待。
private static async Task ThrowExceptionAsync() { await Task.Delay(1000); throw new Exception("拋個異常玩玩"); } public static async Task CatchAsyncTaskException() { try { await ThrowExceptionAsync(); } catch (Exception ex) { throw ex; } } TaskScheduler.UnobservedTaskException
未捕獲的 Task 異常信息可以通過設置全局的TaskScheduler.UnobservedTaskException 來記錄錯誤日志,在 Global.asax 增加如下代碼:
void Application_Start(object sender, EventArgs e) { // 在應用程序啟動時運行的代碼 TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskExceptionException; } void TaskScheduler_UnobservedTaskExceptionException(object sender, UnobservedTaskExceptionEventArgs e) { if (e.Exception != null) { // do something } }
同步上下文
異步編程必然是關于線程的使用,線程有一個同步上下文的概念,個人認為線程同步上下文是 async/await 遇到最揪心的問題。在現有項目開發中我們可能想嘗試使用 async/await,但老代碼都是同步方式,這時如果調用一個聲明為 async 的方法,死鎖和應用程序崩潰的問題一不小心就可能出現。
注意:控制臺程序和.Net Core程序 將不會遇到這個問題,它們不需要同步上下文。
死鎖
private static async Task XXXAsync() { await Task.Delay(1000); // some code } public static void Test() { var task = XXXAsync(); task.Wait(); }
以上代碼很完美的實現了死鎖。 默認情況下,當 Wait() 未完成的 Task 時,會捕獲當前線程上下文,在 Task 完成時使用該上下文恢復方法的執行。 當 async 方法內的 await 執行完成時,它會嘗試獲取調用者線程所在的上下文執行方法的剩余部分, 但是該上下文已含有一個線程,該線程在等待 async 方法完成。然后它們相互等待對方,然后就沒有然后了,死在那里。
針對死鎖問題的解決方式是增加 ConfigureAwait(false)
// await Task.Delay(1000); await Task.Delay(1000).ConfigureAwait(false); // 解決死鎖
當 await 等待完成時,它會嘗試在線程池上下文中執行 async 方法的剩余部分,因此就不存在死鎖。
應用程序崩潰
測試環境總發現IIS應用程序池總是崩潰,到底是什么原因?當時我們對這個問題也是非常懵逼,代碼看上去并沒什么明顯毛病,試圖欺騙自己應該是環境本身的問題吧,但事實上確實是在代碼中下了毒。通過各種資料查閱和測試,基本可以斷定是同步代碼調用異步代碼,同步上下文引起的問題。
如果調用一個 async 方法。如果使用 await 等待,當前線程立馬被釋放回線程池,線程的上下文信息會被保存。如果沒有使用 await(async void 的方法,必然沒有辦法使用 await),調用 async 方法之后,代碼會繼續往下執行,執行完成后當前線程被釋放回線程池,線程的上下文信息不會被保存。當 async 中的異步任務執行完成后,會從線程池中獲取一個線程繼續執行剩余代碼,同時會獲取當初調用者所在線程的上下文信息(如果當初調用者所在線程沒有釋放回線程池,上下文信息可以獲取到)。那么問題就來了,如果當初調用者沒有使用 await 并且 所在線程釋放回線程池了,上下文信息因為沒有被保持下來,就獲取不到了,這時候會拋出異常 未將對象引用設置到對象的實例 ,經過測試這個異常信息并不一定每次都會出現,原因和線程的釋放有關,調用者所在線程的上下文信息存在就不會拋出異常。異常錯誤信息如下,這個異常最終會導致應用程序集停止。
在 System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext) 在 System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext) 在 System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state) 在 System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state) 在 System.Web.LegacyAspNetSynchronizationContext.Post(SendOrPostCallback callback, Object state) 在 System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.PostAction(Object state) 在 System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask) --- 引發異常的上一位置中堆棧跟蹤的末尾 --- 在 System.Threading.Tasks.AwaitTaskContinuation.<>c.<ThrowAsyncIfNecessary>b__18_0(Object s) 在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 在 System.Threading.ThreadPoolWorkQueue.Dispatch() 在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
針對以上異常我們有什么方式可以解決呢?依然是ConfigureAwait(false),在 Task 上加上 ConfigureAwait(false),此設置代表當 async 中的異步任務完成后,不讀取當時調用它的原線程的上下文信息,而是在線程池上下文中執行 async 方法的剩余部分。
public static Task XXXAsync() { await Task.Run(() => { // some code }).ConfigureAwait(false); }
以上是“ASP.NET如何使用async/await”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。