Simultaneous requests to a HTTP Handler not workin

2019-05-10 05:02发布

问题:

I have a Generic HTTP Handler (*.ashx) in my ASP.Net application which performs some basic but time consuming computations printing progress statements to the output as it goes to keep the user informed. Performing these computations involves reading some data files which are locked by the handler while using them, so it's important that two calls to the Handler don't start processing at once.

To achieve this I added a variable into the Cache that indicates the computation is in progress, this prevents the main application sending a user to this Handler if another user is already there. In the Handler itself it checks if the Cache variable is set and should send the user back to the main application if the Cache value is set. But when I test this by accessing the Handler twice one access executes fine and the second sits there and does nothing until the first finishes when it runs. Setting IsReusable to true makes no difference.

Anyone got any ideas why this happens?

Code below:

public class UpdateStats : IHttpHandler
{
    private HttpContext _context;

    public const String UpdateInProgressCacheKey = "FAHLeagueWebUpdateInProgress";

    public void ProcessRequest(HttpContext context)
    {
        //Use a Cache variable to ensure we don't call multiple updates
        Object inprogress = context.Cache[UpdateInProgressCacheKey];
        if (inprogress != null)
        {
            //Already updating
            context.Response.Redirect("Default.aspx");
        }
        else
        {
            //Set the Cache variable so we know an Update is happening
            context.Cache.Insert(UpdateInProgressCacheKey, true, null, DateTime.Now.AddMinutes(10), Cache.NoSlidingExpiration);
        }

        context.Response.Clear();
        context.Response.ContentType = "text/html";
        this._context = context;

        context.Response.Write("<pre>Please wait while we Update our Statistics, you will be automatically redirected when this finishes...\n\n");

        //Get the Stats
        Statistics stats = new Statistics(context.Server);

        //Subscribe to Update Progress Events
        stats.UpdateProgress += this.HandleUpdateProgress;

        //Update
        String force = context.Request.QueryString["force"];
        stats.UpdateStats((force != null));

        //Remove the Cache variable
        context.Cache.Remove(UpdateInProgressCacheKey);

        context.Response.Write("</pre>");
        context.Response.Write("<meta http-equiv=\"refresh\" content=\"0;URL=Default.aspx\" />");
        context.Response.Write("<p>If you are not automatically redirected please click <a href=\"Default.aspx\">here</a></p>");
    }

    private void HandleUpdateProgress(String message)
    {
        this._context.Response.Write(message + "\n");
        this._context.Response.Flush();
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Edit

Added the code from the Master Page of the main application:

public partial class FAH : System.Web.UI.MasterPage
{
    private Statistics _stats;

    protected void Page_Init(object sender, EventArgs e)
    {
        this._stats = new Statistics(this.Server);
        if (this._stats.StatsUpdateNeeded)
        {
            //If the Cache variable is set we're already updating
            Object inprogress = Cache[UpdateStats.UpdateInProgressCacheKey];
            if (inprogress != null) this.Response.Redirect("UpdateStats.ashx");
        }
    }
    //etc...
}

回答1:

Stumbled across the answer myself, it's nothing to do with the web server or the application but merely to do with browser behaviours. It seems that if you open several tabs and navigate to the same URL in a browser like Firefox or Chrome the browser makes the requests sequentially i.e. it waits for one to finish before making the next. Opening two browsers and making the two requests results in the expected behaviour

IIS, Asp.NET pipeline and concurrency



回答2:

If both threads read the cache value inprogress before one of them sets it, then inprogress will be null in both cases.

I think you may need some locking here as you might have some concurrency issues with the above code.



回答3:

Are you sure your webserver is multithreaded and can execute the page simultaneously? Could you add print statements at the start of the ProcessRequest and at the end and see if you get in there simultaneously?