2009-07-30 4 views
2

En C#, j'ai la syntaxe suivante extrêmement verbeux pour tirer une simple liste d'éléments à partir d'une base de données:Comment faire pour que la syntaxe de verrouillage cochée double soit moins verbeuse?

if (malls == null) 
{ 
    lock (_lock) 
    { 
     if (malls == null) 
     { 
      using (var session = NhibernateHelper.OpenSession()) 
      { 
       malls = session.CreateCriteria<Mall>() 
        .AddOrder(Order.Asc("Name")).List<Mall>(); 

       CacheManager.Set(CACHE_KEY, malls, TimeSpan.FromMinutes(CACHE_DURATION)); 
      } 
     } 
    } 
} 

Je suis au courant des avantages de verrouillage revérifié et je soutiens fortement son utilisation, mais semble incroyablement verbeux. Pouvez-vous recommander des raccourcis de syntaxe ou des styles qui pourraient le nettoyer un peu?

+0

Je serais intéressé par les avantages supposés de ce modèle. D'après mon expérience, les gens ont mal compris ce schéma et ses implications (par exemple, cela ne fonctionne pas de manière fiable sur Mono en raison de différences de modèles de mémoire). Je n'ai jamais vu ses avantages supposés à la hauteur de tous les problèmes qu'il provoque. – JaredPar

+0

La méthode en question tirera environ 5000 enregistrements d'une base de données à mettre en mémoire cache. Il va être utilisé dans un site ASP.NET à fort trafic et peut être appelé de plusieurs centaines à plusieurs milliers de fois par seconde, donc je veux définitivement mettre en cache les données et en même temps éviter un autre thread re-tirant les données simplement parce que le La vérification null a échoué avant l'acquisition du verrou. En outre, en raison de l'accès lourd à la fonction, je ne peux pas simplement supprimer la première vérification nulle, car le verrouillage inutile qui en résulte enterrerait l'application. – Chris

+0

@Chris, avez-vous ** prouvé ** c'est en fait un avantage pour votre application? Ou que l'utilisation d'un verrou simple est un problème? – JaredPar

Répondre

8

Vous utilisez vraisemblablement le verrouillage double-vérifié car vous disposez d'une ressource que vous souhaitez initialiser d'une manière paresseuse, threadsafe. Pour ce faire, le verrouillage à double-contrôle est un mécanisme , mais comme vous l'avez noté correctement, la verbosité du mécanisme est en train de surestimer la signification du code. Lorsque vous avez un mécanisme qui obscurcit la signification, cache le mécanisme en créant une abstraction. Une façon de le faire serait de créer une classe d'instanciation "paresseux threadsafe" et de lui passer un délégué qui fait l'opération que vous souhaitez faire d'une manière paresseuse, threadsafe.

Cependant, il existe une meilleure solution. La meilleure façon est de ne pas faire ce travail vous-même, mais plutôt de laisser un expert de classe mondiale sur le filetage le faire pour vous. De cette façon, vous n'avez pas à vous soucier de bien faire les choses. Joe Duffy doit s'inquiéter de bien faire les choses. Comme Joe le dit sagement, plutôt que de répéter le mécanisme de verrouillage partout, écrivez-le une fois et ensuite utilisez l'abstraction.

Code de Joe est ici:

http://www.bluebytesoftware.com/blog/PermaLink,guid,a2787ef6-ade6-4818-846a-2b2fd8bb752b.aspx

et une variation de ce code sera disponible dans la prochaine version de la bibliothèque de classes de base.

+0

Impressionnant code à la recherche. Je vais essayer ça, merci! – Chris

+0

Est-ce que MS paye des gens comme Joe pour du code comme ça? Juste intéressé .. – flesh

+3

Par "gens comme Joe", voulez-vous dire "Microsoft employés"? Oui, nous payons nos employés - comme Joe - pour écrire du code. :-) –

1

Pour réduire le bruit que vous pouvez faire ceci:

public List<Mall> Malls() 
{ 
    EnsureMallsInitialized(); 
    return malls; 
} 

private void EnsureMallsInitialized() 
{ 
    if (malls == null) // not set 
    lock (_lock)  // get lock 
    if (malls == null) // still not set 
    { 
     InitializeMalls(); 
    }   
} 

private void InitializeMalls() 
{ 
    using (var session = NhibernateHelper.OpenSession()) 
    { 
     malls = session.CreateCriteria<Mall>() 
      .AddOrder(Order.Asc("Name")).List<Mall>(); 

     CacheManager.Set(CACHE_KEY, malls, TimeSpan.FromMinutes(CACHE_DURATION)); 
    } 
}