81 votes

Quelle est la meilleure façon de verrouiller le cache en asp.net ?

Je sais que dans certaines circonstances, par exemple dans le cas de processus longs, il est important de verrouiller le cache d'ASP.NET afin d'éviter que les demandes ultérieures d'un autre utilisateur pour cette ressource n'exécutent à nouveau le processus long au lieu d'atteindre le cache.

Quelle est la meilleure façon d'implémenter le verrouillage du cache en c# dans ASP.NET ?

117voto

a7drew Points 4005

Voici le modèle de base :

  • Vérifie la valeur dans le cache, retourne si elle est disponible.
  • Si la valeur n'est pas dans le cache, alors il faut implémenter un verrou.
  • Dans la serrure, vérifiez à nouveau le cache, vous avez peut-être été bloqué.
  • Effectuer la recherche de la valeur et la mettre en cache
  • Libérer la serrure

En code, cela ressemble à ceci :

private static object ThisLock = new object();

public string GetFoo()
{

  // try to pull from cache here

  lock (ThisLock)
  {
    // cache was empty before we got the lock, check again inside the lock

    // cache is still empty, so retreive the value here

    // store the value in the cache here
  }

  // return the cached value here

}

4 votes

Si le premier chargement du cache prend quelques minutes, y a-t-il encore un moyen d'accéder aux entrées déjà chargées ? Disons que si j'ai GetFoo_AmazonArticlesByCategory(string categoryKey). Je suppose que ce serait quelque chose comme un verrou par categoryKey.

5 votes

Appelé "verrouillage doublement vérifié". fr.wikipedia.org/wiki/Double-checked_locking

32voto

John Owen Points 897

Pour être complet, un exemple complet ressemblerait à ceci.

private static object ThisLock = new object();
...
object dataObject = Cache["globalData"];
if( dataObject == null )
{
    lock( ThisLock )
    {
        dataObject = Cache["globalData"];

        if( dataObject == null )
        {
            //Get Data from db
             dataObject = GlobalObj.GetData();
             Cache["globalData"] = dataObject;
        }
    }
}
return dataObject;

7 votes

If( dataObject == null ) { lock(ThisLock) { if( dataObject == null ) // bien sûr, il est toujours nul !

30 votes

@Constantin : pas vraiment, quelqu'un pourrait avoir mis à jour le cache pendant que vous attendiez d'acquérir le lock()

12 votes

@John Owen - après la déclaration de verrouillage, vous devez essayer de récupérer l'objet dans le cache à nouveau !

13voto

user378380 Points 115

Pour faire écho à ce que Pavel a dit, je pense que c'est la façon la plus sûre de l'écrire.

private T GetOrAddToCache<T>(string cacheKey, GenericObjectParamsDelegate<T> creator, params object[] creatorArgs) where T : class, new()
    {
        T returnValue = HttpContext.Current.Cache[cacheKey] as T;
        if (returnValue == null)
        {
            lock (this)
            {
                returnValue = HttpContext.Current.Cache[cacheKey] as T;
                if (returnValue == null)
                {
                    returnValue = creator(creatorArgs);
                    if (returnValue == null)
                    {
                        throw new Exception("Attempt to cache a null reference");
                    }
                    HttpContext.Current.Cache.Add(
                        cacheKey,
                        returnValue,
                        null,
                        System.Web.Caching.Cache.NoAbsoluteExpiration,
                        System.Web.Caching.Cache.NoSlidingExpiration,
                        CacheItemPriority.Normal,
                        null);
                }
            }
        }

        return returnValue;
    }

7 votes

lock(this)` est est mauvais . Vous devez utiliser un objet de verrouillage dédié qui n'est pas visible par les utilisateurs de votre classe. Supposons que, plus tard, quelqu'un décide d'utiliser l'objet de cache pour verrouiller. Il ne sera pas conscient qu'il est utilisé en interne à des fins de verrouillage, ce qui peut conduire à une mauvaise chose.

2voto

khebbie Points 961

Craig Shoemaker a réalisé une excellente émission sur la mise en cache d'asp.net : http://polymorphicpodcast.com/shows/webperformance/

1voto

Seb Nilsson Points 8619

J'ai vu récemment un patron intitulé Correct State Bag Access Pattern, qui semblait aborder ce sujet.

Je l'ai modifié un peu pour qu'il soit sécurisé.

http://weblogs.asp.net/craigshoemaker/archive/2008/08/28/asp-net-caching-and-performance.aspx

private static object _listLock = new object();

public List List() {
    string cacheKey = "customers";
    List myList = Cache[cacheKey] as List;
    if(myList == null) {
        lock (_listLock) {
            myList = Cache[cacheKey] as List;
            if (myList == null) {
                myList = DAL.ListCustomers();
                Cache.Insert(cacheKey, mList, null, SiteConfig.CacheDuration, TimeSpan.Zero);
            }
        }
    }
    return myList;
}

0 votes

Deux threads ne pourraient-ils pas tous deux obtenir un résultat vrai pour (myList==null) ? Ensuite, les deux threads font un appel à DAL.ListCustomers() et insèrent les résultats dans le cache.

4 votes

Après le verrouillage, vous devez vérifier à nouveau le cache, et non pas les données locales. myList variable

1 votes

C'était en fait correct avant votre modification. Aucun verrou n'est nécessaire si vous utilisez Insert pour empêcher les exceptions, seulement si vous voulez vous assurer que DAL.ListCustomers est appelé une fois (mais si le résultat est nul, il sera appelé à chaque fois).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X