會話Session
- Session用於服務器端狀態管理,使用Session之后,每個客戶端都可以將實際的數據保存在服務器上,對於每個客戶端的數據,將會生成一個對應的唯一的key(保存在客戶端)。客戶端與服務器端就是通過這個key來確認客戶端的身份,通常這個key為SessionID。
- 一般情況下,SessionID以Cookie的形式保存在瀏覽器中,在不使用Cookie的情況下,也可以將這個SessionID嵌入到訪問網頁的URL中。
服務器端Session
在頁面對象或者HttpContext對象中,都有一個名為Session的屬性,在一次會話中,它們引用的都是同一個對象。
public HttpSessionState Session { get; }
Session對象是HttpSessionState類的實例。Session是保存在服務器端的,對每個登錄到網站的用戶都有一份,是獨有的,而其他用戶無法共享。
那么問題來了,來看看奇怪的阻塞。
我們來看一個一需求: 需要實時的將服務器的運行狀態輸出到當前登陸的客戶端,建立長連接並且作實時輸出。 然后就有了下面這個Action。

Boolean isOnline = true; public ActionResult Index() { #region 滾動條控制 Response.Write("<html onclick=\"clearInterval(i_1);\" ondblclick =\"reInterval()\"><head><title>服務器實時監控</title></head>"); Response.Write("<script type=\"text/javascript\">"); Response.Write("function scrollWindow() { document.body.scrollTop = document.body.scrollHeight; }"); Response.Write("function reInterval() { i_1 = setInterval('scrollWindow()', 50); }"); Response.Write("i_1 = setInterval('scrollWindow()', 50);"); Response.Write("scrollWindow();"); Response.Write("</script> "); Response.Flush(); #endregion Dictionary<int, string> dicColor = new Dictionary<int, string>() { {0,"#0000FF"}, // 藍色 {1,"#FF3333"}, // 紅色 {2,"#FFFF00"}, // 黃色 {3,"#FF3EFF"}, {4,"#0000FF"}, {5,"#0000FF"} }; DebugCallback callback = new DebugCallback(delegate(int type, string str) { if (dicColor.ContainsKey(type)) { Response.Write("<span style = 'color:" + dicColor[type] + ";'>" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "--" + str + "</span> <br />"); } Response.Flush(); }); Log.AddCallBack(callback); while (isOnline) { try { Response.Write("...<br>"); Response.Flush(); } catch { } System.Threading.Thread.Sleep(1000); if (!Response.IsClientConnected) // 連接關閉 { Log.RemoveCallBack(callback); Response.Write("</html>"); break; } } return null; }
由於這個Action是一個長連接,它不會斷開連接會一直處於請求的狀態,這個時候當我們再請求同一個Session的其它的Action的時候,發現所有其它的Action都會處於Waiting狀態……好吧,明明是異步並發請求,為何現在卻成了“單線程”工作了呢?很明顯有鎖。
原因
- HttpSessionState來自於HttpModule的SessionStateModule。在每次請求處理過程中,HttpApplication的請求的處理管道中會檢查當前請求的處理程序是否實現了接口IRequiresSessionState,如果實現的話,那么SessionStateModule將為這個請求分配HttpSessionState。同時SessionStateModule還負責SessionID的生成、Cookieless會話管理、從外部狀態提供程序中檢索會話數據以及將數據綁定到請求的調用上下文。
- 如果頁面請求設置一個讀取器鎖定,同一會話中同時處理的其他請求將無法更新會話狀態,但是至少可以進行讀取。如果頁面請求為會話狀態設置一個寫入鎖,那么所有其他頁面都被阻止,無論他們是否要讀取或寫入內容。例如,如果同時有兩段程序視圖在同一個Session中寫入內容,一段程序必須等到另一段程序完成后才能寫入。在AJAX程序設計中,必須注意這種情況的發生。
解決方法
對於Asp.net MVC:
可以為本Controller增加以下特性,但是本Controller都不能修改Session了,只能讀取
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
對於Asp.net WebForm:
在Web.config 文件里面添加
EnableSessionState="ReadOnly" // 僅僅加載那個阻塞頁面