2009-06-12 8 views
3

J'ai un gestionnaire HTTP générique (* .ashx) dans mon application ASP.Net qui effectue des calculs de base, mais qui prennent du temps, en imprimant des instructions de progression à la sortie pour que l'utilisateur reste informé . L'exécution de ces calculs implique la lecture de certains fichiers de données qui sont verrouillés par le gestionnaire lors de leur utilisation, il est donc important que deux appels au gestionnaire ne démarrent pas le traitement à la fois. Pour y parvenir, j'ai ajouté une variable dans le cache qui indique que le calcul est en cours, ce qui empêche l'application principale d'envoyer un utilisateur à ce gestionnaire si un autre utilisateur s'y trouve déjà. Dans le gestionnaire lui-même, il vérifie si la variable cache est définie et doit renvoyer l'utilisateur à l'application principale si la valeur du cache est définie. Mais quand je teste ceci en accédant au Handler deux fois, un accès s'exécute bien et le second reste là et ne fait rien jusqu'à ce que le premier se termine quand il s'exécute. Définir IsReusable sur true ne fait aucune différence.Les demandes simultanées à un gestionnaire HTTP ne fonctionnent pas

Quelqu'un at-il des idées pourquoi cela se produit?

code ci-dessous:

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; 
     } 
    } 
} 

Modifier

Ajouté le code de la page maître de l'application principale:

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... 
} 

Répondre

6

Je suis tombé moi-même sur la réponse, cela n'a rien à voir avec le serveur Web ou l'application, mais uniquement avec les comportements du navigateur. Il semble que si vous ouvrez plusieurs onglets et que vous naviguez vers la même URL dans un navigateur comme Firefox ou Chrome, le navigateur effectue les requêtes de manière séquentielle, c'est-à-dire qu'il attend que l'une finisse avant de passer à la suivante. L'ouverture de deux navigateurs et de faire les deux demandes résultats dans le comportement attendu

IIS, Asp.NET pipeline and concurrency

0

Si les deux fils lire la valeur du cache InProgress avant une d'entre eux le définit, alors en cours d'exécution sera nulle dans les deux cas.

Je pense que vous pourriez avoir besoin d'un verrouillage ici, car vous pourriez avoir des problèmes de concurrence avec le code ci-dessus. Etes-vous sûr que votre serveur Web est multithread et peut exécuter la page simultanément?

+0

Je suppose que cela pourrait se produire, mais cela ne ferait que donner lieu à l'application principale de rediriger les requêtes vers le gestionnaire de toute façon, car si la mise à jour ne fonctionne pas arrive alors l'application principale voudra toujours une mise à jour – RobV

+0

Je pense qu'avoir les deux threads avec un cache nul signifie que vous pouvez frapper context.Cache.Insert deux fois, donc vous ne bloquerez pas la deuxième requête. Mais ce n'est pas le problème que vous décrivez. Il peut également y avoir un problème de synchronisation similaire avec la ligne "if (inprogress! = Null) this.Response.Redirect (" UpdateStats.ashx ");" car cela devra faire un aller retour au navigateur pour rediriger vers le gestionnaire. Comment faites-vous vos 2 demandes de test? est-ce dans le même navigateur? –

0

Etes-vous sûr que votre serveur Web est multithread? Pourriez-vous ajouter des instructions print au début de ProcessRequest et à la fin et voir si vous y arrivez simultanément?

+0

Lors du débogage à l'aide de VS2008, la seconde requête ne tombera même pas dans la méthode ProcessRequest jusqu'à ce que la première se termine. Je cours IIS7 sur Windows Server 2008 sur ma boîte de développement de noyau de quadruple ainsi je suis multiplié fortement – RobV

+0

Ce n'est pas parce que votre unité centrale de traitement ou OS est multithread que votre serveur de Web est. Étant donné que la deuxième requête ne vient pas dans la méthode ProcessRequest avant la fin du premier, il est évident que le serveur traite les demandes de manière séquentielle.C'est une limitation de l'application serveur, peut-être qu'il peut être configuré, je n'ai pas d'expérience avec ça. Vous pouvez essayer de voir comment il se comporte avec des requêtes simultanées sur deux pages différentes, peut-être que la gestion séquentielle ne se fait que par page. Cela signifie également que votre mécanisme en cours est "inutile" pour le moment. – Ron

Questions connexes