Why can’t I execute two requests from the same session simultaneously for an ASP.NET application?

Many times we need pop-up windows to appear in our Web Applications. In such a scenario we have the same user session used by the two instances of the web browser. So for N number of pop-ups we open, we have N instances of browsers using the same user session.

So the question which will come to mind is, will these requests using the same user session be executed concurrently?

The answer to the question would be - maybe.

The request execution will work fine concurrently until the time we are not accessing session data at all. If we would have accessed the session data anywhere in the request then the requests won’t be served concurrently. Requests will be executed one at a time.

We can check this behavior with a simple demo –

Have 2 pages in an ASP.NET application as Default.aspx and LongRunningPage.aspx as below –

Default.aspx

<%@ Page Language="C#" %>
<html>
<head runat="server">
<title>Default Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<a href="LongRunningPage.aspx?interval=50000" mce_href="LongRunningPage.aspx?interval=50000" target="_blank">Interval 50000</a>
<br /><br />
<a href="LongRunningPage.aspx?interval=1000" mce_href="LongRunningPage.aspx?interval=1000" target="_blank">Interval 1000</a>
</div>
</form>
</body>
</html>

LongRunningPage.aspx

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
int interval = Convert.ToInt32(Request.QueryString["interval"].ToString());
System.Threading.Thread.Sleep(interval);
}
</script>

<html xmlns="https://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Long Running Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Displayed!!!
</div>
</form>
</body>
</html>

Now when you click on the link “Interval 50000” on Default.aspx the first page will come up and will wait for 50000ms. Meanwhile if you click on the link “Interval 1000” it will come up after waiting for 1000ms.

Now clear the browser cache and change the Page_Load for LongRunningPage.aspx as below –

protected void Page_Load(object sender, EventArgs e)
{
int interval = Convert.ToInt32(Request.QueryString["interval"].ToString());
Session["Test"] = "Test";
System.Threading.Thread.Sleep(interval);
}

Now when you click on the link “Interval 50000” on Default.aspx the first page will come up and will keep waiting for 50000ms. Meanwhile if you click on the link “Interval 1000” it won’t come up until the first page comes up (opened by clicking on the link “Interval 50000”) and then it will wait for 1000ms and then it will be rendered.

This is expected behavior. The reason behind this is session state implements a reader/writer locking mechanism:

- A page (or frame) that has session state write access (e.g. <%@ Page EnableSessionState="True" %>) will by default hold a writer lock on the session until the request finishes.

- A page (or frame) that has session state read access (e.g. <%@ Page EnableSessionState="ReadOnly" %>) will hold only a reader lock on the session until the request finishes.

- As we can understand based on concepts of locks – a Reader lock will block a writer lock; a Reader lock will NOT block a reader lock; a Writer lock will block all reader and writer locks.

This lock is acquired within the event AcquireRequestState and released in the event ReleaseRequestState of a request. That's why if two frames/pages both have session state write access (the default option unless you use EnableSessionState="ReadOnly" ), one frame/page has to wait for the other to finish.

So the work around for your problem would be to use the ReadOnly Session access mode (for example using EnableSessionState="ReadOnly" ) if you have the requirement of only reading the session state, or else you have to make sure the requests are NOT long running.

Additionally you will see similar behavior when you have a Global.asax in the application. By default if you do not have a Global.asax file in an ASP.Net application, and there is no access to any session reading/writing, then effectively no session exists from an ASP.Net perspective. One trigger to make ASP.net create session infrastructure is to have a Global.asax file and have events in it regarding Session start, end etc. This will trigger the sessions to be created and the request ultimately accesses the session via session events. We will see similar behavior when there is a Global.asax in the application and you fire a next request for this same session: it will be put on hold until the first request completes.

I hope this help. Happy Reading!!!