默認情況下,當您使用async/await時,它將在開始請求的原始線程上繼續運行(狀態機)。
但是,如果當前另一個長時間運行的進程已經接管了該線程,那么你就不得不等待它完成。要避免這個問題,可以使用ConfigureAwait的方法和false參數。當你用這個方法的時候,這將告訴Task它可以在任何可用的線程上恢復自己繼續運行,而不是等待最初創建它的線程。這將加快響應速度並避免許多死鎖。
但是,這里有一點點損失。當您在另一個線程上繼續時,線程同步上下文將丟失,因為狀態機改變。這里最大的損失是你會失去歸屬於線程的Culture和Language,其中包含了國家語言時區信息,以及來自原始線程的HttpContext.Current之類的信息,因此,如果您不需要以此來做多語系或操作任何HttpContext類型設置,則可以安全地進行此方法的調用。注意:如果需要language/culture,可以始終在await之前存儲當前相關狀態值,然后在await新線程之后重新應用它。
注意事項
如果有同步方法調用異步方法,則必須使用ConfigureAwait(false)。如果不這樣做,就會立即掉進死鎖陷阱。
發生的情況是主線程將調用async方法,最終會阻塞這個線程,直到那個async方法完成。然而,一旦異步方法完成,它必須等待原始調用者完成后才能繼續。他們都在等待對方完成,而且永遠不會。通過在調用中使用configurewait (false), async方法將能夠在另一個線程上完成自己操作,而不關心自己的狀態機的位置,並通知原始線程它已經完成。
死鎖舉例

public class HomeController : Controller { public ActionResult Index() { DoAsync().Wait();//同步調用1,發生死鎖 //var r = DoAsync().Result;//同步調用2,發生死鎖 return View(); } public async Task<int> DoAsync() { await Task.Delay(2000).ConfigureAwait(true);//默認就是ture return 1; } }
探討.NetCore中異步注意事項
在.NetCore中已經剔除了SynchronizationContext,剔除他的主要原因主要是性能和進一步簡化操作
在.NetCore中我們不用繼續關心異步同步混用情況下,是否哪里沒有設置ConfigureAwait(false) 會導致的死鎖問題,因為在.netcore中的async/await 可能在任何線程上執行,並且可能並行運行!
如下代碼,在舊版ASP.NET(.NetFramework)中工作正常,而ASP.NET Core上不是線程安全的

public class HomeController : Controller { public async Task<ActionResult> Index() { var result = await GetBothAsync(); return View(); } async Task<List<string>> GetBothAsync() { var result = new List<string>(); var task1 = GetOneAsync(result); var task2 = GetOneAsync(result); await Task.WhenAll(task1, task2); return result; } async Task GetOneAsync(List<string> result) { await Task.Delay(2000); for (int i = 0; i < 10 * 10 * 10 * 10 * 10; i++) { result.Add(i.ToString()); } } }
此代碼在舊版ASP.NET(.NetFramework)中工作正常,由於請求處設置了await,請求上下文一次只允許一個連接.
其中result.Add(data)
一次只能由一個線程執行,因為它在請求上下文中執行。(可以理解為在源線程執行,是吧?QAQ)
但是,這個相同的代碼在ASP.NET Core上是不安全的; 具體地說,該result.Add(data)
行可以由兩個線程同時執行,而不保護共享List<string>
。
所以在.Netcore中要特別注意異步代碼在並行執行情況下引發的問題
源博客地址1