2016-07-06 57 views
0

閱讀這兩篇文章,SynchronisationContextAsync/Await。我非常熟悉ASP.NET SynchronisationContext處理將異步方法執行重新輸入請求線程的方式。在ASP.NET中使用默認的ThreadPool SynchronisationContext

一個與再入大問題是,如果你使用.Wait().Result可以導致死鎖,原因是:

當的await完成後,它會嘗試執行異步方法的其餘部分在捕獲的上下文中。但是該上下文已經有一個線程,它正在(同步)等待異步方法完成。

你得到,因爲正在使用由SynchronisationContext和方式單一線程的這一點,permits only one chunk of code to run at a time.

什麼是使用Task.Factory.StartNew和分配給它的ASP.NET應用程序中的ThreadPoolSynchronisationContext的影響?

我不會爲什麼要這麼做而煩惱,而更多會發生什麼?

我知道你不應該在ASP.NET中使用Fire-and-Forget異步操作,因爲在異步操作完成之前可能會清除線程。在ThreadPoolSynchronisationContext的情況下,情況也是如此。

回答

1

在ASP.NET應用程序中使用Task.Factory.StartNew併爲其分配ThreadPool SynchronisationContext會有什麼影響?

沒有理由使用StartNew;幾乎在每個用例中,Task.Run都是優越的。此外,您不需要「分配」線程池SynchronizationContext,因爲線程池線程自然具有該線程池。

所以,當然,你可以做到這一點:

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

和所有的...代碼將在一個線程池線程上運行(請求上下文之外),和線程池線程恢復(在請求上下文之外)。

這個用法也沒有防火的問題,因爲我們的代碼是await的結果。

但我一般不鼓勵這種對ASP.NET,因爲想想它在做什麼:

  • 代碼隊列工作(...)線程池。
  • 然後它異步地等待那項工作完成。在此期間,原始請求線程返回到線程池。
  • 工作完成後,調用方法在請求上下文中繼續。

因此,它會導致額外的線程切換,同時不會給您帶來任何好處。這是可能有少量的並行性,但那麼你是在談論一些潛在的嚴重的可擴展性限制。每當我在生產環境中完成ASP.NET的並行處理時,我最終都會將其剔除。

我知道你絕對不應該在ASP.NET中使用Fire-and-forget異步操作,因爲在異步操作完成之前可能會清除線程。這在ThreadPool SynchronisationContext的情況下也是如此。

實際上,這並不是因爲特定的線程被異常終止。這是因爲整個AppDomain被拆除了。這包括中止全部線程。

所以,是的,在ASP.NET,使用線程池,就像任何其他種類的壞發射後不管射後不理:

Task.Run(() => ...); // bad! 

最起碼,你應該註冊與ASP.NET運行時間的工作(而不是使工作在真正意義上的可靠性 - 它只有最小化工作將被中止的機會)。現代ASP.NET有這個HostingEnvironment.QueueBackgroundWorkItem

在ASP.NET添加此方法之前,我有一個庫,它以非常類似的方式註冊了工作。 I used Task.Run用於在任何請求上下文之外運行已註冊的工作並使用線程池SynchronizationContext

+0

那麼,調用'Task.Run'上的'.Result'和'.Wait()'效果仍然是一樣的,因爲執行其餘工作的同步上下文仍然一次執行一個因爲它是ASP.NET上下文 –

+0

我認爲'Task.Run'會使用捕獲的上下文,但是我可以從[源代碼](http://referencesource.microsoft.com/#mscorlib/system /thread/Tasks/Task.cs,89fc01f3bb88eed9)這是[ThreadPoolTask​​Scheduler](http://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/TaskScheduler.cs,495c69a13f3b023f) –

+0

@CallumLinington:如果您阻止從'Task.Run'返回的任務,那麼你會在請求上下文中阻塞一個線程。而且,當這項工作正在進行而不是一項工作時,您將使用兩個線程。 –