Asp.net 为什么在Global.asax.cs中启动会话_会导致性能问题?

Asp.net 为什么在Global.asax.cs中启动会话_会导致性能问题?,asp.net,global-asax,Asp.net,Global Asax,当我在Global.asax.cs中创建一个空的Session_Start处理程序时,它会在将页面呈现到浏览器时造成重大影响 如何复制: 创建一个空的ASP.NET MVC 3 web应用程序(我使用的是MVC 3 RC2)。 然后使用以下代码添加主控制器: public class HomeController : Controller { public ActionResult Index() { return View(); } public ActionResul

当我在Global.asax.cs中创建一个空的Session_Start处理程序时,它会在将页面呈现到浏览器时造成重大影响

如何复制:

创建一个空的ASP.NET MVC 3 web应用程序(我使用的是MVC 3 RC2)。 然后使用以下代码添加主控制器:

public class HomeController : Controller
{
  public ActionResult Index()
  {
    return View();
  }
  public ActionResult Number(int id)
  {
    return Content(id.ToString());
  }
}
接下来创建一个视图Home/Index.cshtml,并将以下内容放在正文部分:

@for (int n = 0; n < 20; n++)
{ 
  <iframe src="@Url.Content("~/Home/Number/" + n)" width=100 height=100 />
}
现在再次运行页面。这一次,您会注意到20个iFrame的加载速度要慢得多,一个接一个,间隔约1秒。这很奇怪,因为我们在会话开始时没有做任何事情。。。这只是一个空方法。但这似乎足以导致所有后续页面的速度减慢

有人知道为什么会发生这种情况吗?更好的是,有人有解决方法吗

更新


我发现,只有在附加了调试器(使用F5运行)时才会发生这种行为。如果在没有附加调试程序(Ctrl-F5)的情况下运行它,那么它看起来是正常的。因此,这可能不是一个重要的问题,但仍然很奇怪。

无法告诉您调试器正在做什么(intellitrace?详细日志记录?首次机会异常?),但您仍然掌握着会话处理并发请求的能力

对ASP.NET会话状态的访问在每个会话中是独占的,这意味着如果两个不同的用户同时发出请求,则会同时授予对每个单独会话的访问权限但是,如果对同一会话发出两个并发请求(通过使用相同的SessionID值),则第一个请求以独占方式访问会话信息。第二个请求仅在第一个请求完成后执行。(如果由于第一个请求超过锁定超时而释放了对信息的独占锁定,则第二个会话也可以访问。)如果@Page指令中的EnableSessionState值设置为ReadOnly,对只读会话信息的请求不会导致对会话数据的独占锁定。但是,对于会话数据的只读请求可能仍然必须等待会话数据的读写请求设置的锁才能清除


资料来源:,我的重点是tl;dr:如果您在Webforms中遇到此问题,并且不要求对该特定页面中的会话状态具有写访问权限,则将
EnableSessionState=“ReadOnly”
添加到
@page
指令会有所帮助


显然,
Session\u Start
的存在迫使ASP.NET按顺序执行来自同一会话的所有请求。但是,如果您不需要对会话进行写访问(请参见下文),则可以逐页修复此问题

我已经用Webforms创建了自己的测试设置,它使用aspx页面交付图像。1

以下是测试页面(纯HTML,项目的起始页面):

测试运行

  • 运行1,未修改:页面加载速度很快,输出窗口显示的是
    Start
    End
    的随机混合,这意味着请求得到并行处理

  • 运行2,将空的
    Session\u Start
    添加到
    global.asax
    (需要在浏览器中按F5一次,不知道为什么会这样):
    Start
    End
    交替,显示请求是按顺序处理的。多次刷新浏览器表明,即使未连接调试器,也存在性能问题

  • 运行3,与运行2类似,但将
    启用会话状态=“ReadOnly”
    添加到
    GetImage.aspx的
    @Page
    指令中:调试输出在第一个
    结束之前显示多个
    开始
    。我们再次并驾齐驱,我们有着良好的表现



1是的,我知道这应该由ashx处理程序来完成。这只是一个例子。

了解这一点很有意思,但我认为这并不能解释上述行为。案例1和案例2的唯一区别在于Global.asax.cs中存在一个空的Session#u Start方法。根据您的参考,这两种情况的行为应该仍然相同。显然,Global.asax.cs中的空
Session\u Start
方法足以使ASP.NET序列化请求。我在Webforms上也遇到过类似的问题,发现可以通过在为并行页面提供服务的aspx页面的
@Page
指令中设置
EnableSessionState=“ReadOnly”
来解决这个问题(在我的例子中是图像)。我不知道MVC是否有类似的选项。@Heinzi非常有趣!您是否有任何文件/证据显示这种行为?如果是这样,请创建一个新答案,我会将其标记为已接受。@Mike:我还没有找到关于此的文档,但我已将我的发现添加为答案;希望对某人有用。有趣。。。你能测试一下当你在调试器之外运行这些测试时会发生什么吗(例如,用Ctrl-F5而不是F5启动)@Mike:是的。即使没有调试器,运行2也比其他运行程序慢(区别没有调试器那么大,但仍然很明显)。我在SPA应用程序中的AJAX请求中遇到了这个问题。引导我远离使用SessionState。
protected void Session_Start()
{
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title></title></head>
<body>
    <div>
        <img src="GetImage.aspx?text=A" />
        <img src="GetImage.aspx?text=B" />
        <img src="GetImage.aspx?text=C" />
        <img src="GetImage.aspx?text=D" />
        <img src="GetImage.aspx?text=E" />
        <img src="GetImage.aspx?text=F" />
        <img src="GetImage.aspx?text=G" />
        <img src="GetImage.aspx?text=H" />
        <img src="GetImage.aspx?text=I" />
        <img src="GetImage.aspx?text=J" />
        <img src="GetImage.aspx?text=K" />
        <img src="GetImage.aspx?text=L" />
    </div>
</body>
</html>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GetImage.aspx.cs" Inherits="CsWebApplication1.GetImage" %>
public partial class GetImage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Debug.WriteLine("Start: " + DateTime.Now.Millisecond);
        Response.Clear();
        Response.ContentType = "image/jpeg";

        var image = GetDummyImage(Request.QueryString["text"]);
        Response.OutputStream.Write(image, 0, image.Length);
        Debug.WriteLine("End: " + DateTime.Now.Millisecond);
    }

    // Empty 50x50 JPG with text written in the center
    private byte[] GetDummyImage(string text)
    {
        using (var bmp = new Bitmap(50, 50))
        using (var gr = Graphics.FromImage(bmp))
        {
            gr.Clear(Color.White);
            gr.DrawString(text,
                new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular, GraphicsUnit.Point),
                Brushes.Black, new RectangleF(0, 0, 50, 50),
                new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
            using (var stream = new MemoryStream())
            {
                bmp.Save(stream, ImageFormat.Jpeg);
                return stream.ToArray();
            }
        }
    }
}