Why is HttpContext.Current null after await?


今天在對項目代碼進行異步化改進的時候,遇到一個奇怪的問題(莫笑,以前沒遇過),正如標題一樣,HttpContext.Current 在 await 異步執行之后,就會變為 null。

演示代碼:

       public async Task<IEnumerable<string>> Get()
        {
            await DoWaitAsync();
            DoWork();
            return  new string[] { "value1", "value2" };
        }

        private Task DoWaitAsync()
        {
            return
                Task.Factory.StartNew(
                () => 
                {
                    // null !!
                    var httpCtx = System.Web.HttpContext.Current;
                    Thread.Sleep(1000);
                });
        }

        public void DoWork()
        {
            //Not null
            var httpCtx = System.Web.HttpContext.Current;
        }

HttpContext.Current 這個東西,我們並不陌生,在進行 ASP.NET 應用程序開發的時候,我們經常會用到,比如獲取當前請求的一些值,首先它是一個線程靜態屬性(thread-static variable),注意其中的關鍵字:當前請求和線程,也就是說它是和請求線程相關的,在 ASP.NET 應用程序中,一個請求線程會貫穿整個請求過程,所以我們可以在這個請求的任何地方,都可以訪問到 HttpContext.Current,這也就是它的“強大之處”,但是如果涉及到異步多線程呢?就不是這么回事了,因為 HttpContext.Current 依附的是當前請求的主線程,當我們使用 await 異步執行一些代碼的時候,再次執行下面的代碼,其實就不是當前請求線程了,所以我們再次訪問 HttpContext.Current 的時候,就變為 null 了,這個問題告誡我們,ASP.NET 應用程序中,如果進行異步化,使用 HttpContext.Current 一定要小心謹慎。

  • 需要注意的是:HttpContext.Current 在沒有進行 await 操作的時候,都是可以獲取到值的。

如果我們的 ASP.NET 應用程序進行了異步化,然后還必須用到 HttpContext.Current,那我們該怎么解決這個問題?

解決的方式有很多,如果應用程序很簡單,我們可以在 await 操作之前,先用變量存儲 HttpContext.Current,用到的地方直接用這個變量就行了,當然這不是一個“解決問題”的方法,還有一種是用 Cache,可以參考:system-web-httpcontext-current-nulls-itself-after-checking-for-a-cache,我覺得這種方式也是“瞎忽悠”,沒有從根本問題上進行解決。

其實想想問題的根源,就是如何在多個線程中共享一個 HttpContext.Current,這個在 MSDN 中表述為:共享/同步上下文(Synchronization Context)

You can use the TaskScheduler.FromCurrentSynchronizationContext method to specify that a task should be scheduled to run on a particular thread. This is useful in frameworks such as Windows Forms and Windows Presentation Foundation where access to user interface objects is often restricted to code that is running on the same thread on which the UI object was created. For more information, see How to: Schedule Work on a Specified Synchronization Context.

那我們如何在 ASP.NET 應用程序中,進行運用呢?答案很簡單,我們只需要在 web.config 中指定 targetFramework 版本為 4.5 即可:

<httpRuntime targetFramework="4.5" />

或者在 appSettings 中添加如下 key(測試可用):

<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />

參考資料:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM