1

J'ai passé environ une heure à chercher un consensus sur quelque chose que j'essaie d'accomplir, mais je n'ai pas encore trouvé quelque chose de concluant dans une direction particulière.Threadsafe chargement paresseux lorsque le chargement pourrait échouer

Ma situation est la suivante:

  • J'ai une application multi-thread (service Web .NET)
  • J'ai des classes qui utilisent des objets qui prennent du temps non négligeable à la charge, donc je comme pour les maintenir en tant que membres statiques de classe
  • Le code qui construit ces objets a par intermittence un faible risque d'échec

j'utilisais déjà une approche qui construit ces objets dans un constructeur statique. Le problème avec ceci était que, comme mentionné ci-dessus, le constructeur échouait parfois, et une fois qu'un constructeur statique de .NET échoue, la classe entière est arrosée jusqu'à ce que le processus soit relancé. Il n'y a pas de seconde chance avec cette approche.

L'approche qui semblait la plus intuitive était d'utiliser le verrouillage à double contrôle. Il y a beaucoup de pages autour de cette discussion sur les inconvénients du verrouillage à double vérification et dire d'utiliser un constructeur statique, ce que je faisais déjà, mais cela ne semble pas être une option pour moi, car le constructeur statique a le potentiel d'échouer et de faire tomber toute la classe.

La mise en œuvre (simplifiée, bien sûr) que je songe à utiliser est la suivante. Tous les noms de classe et de membre sont purement démonstratifs et pas ce que j'utilise réellement. Cette approche va-t-elle poser problème? Quelqu'un peut-il suggérer une meilleure approche?

public class LazyMembers 
{ 
    private static volatile XmlDocument s_doc; 
    private static volatile XmlNamespaceManager s_nsmgr; 
    private static readonly object s_lock = new object(); 

    private static void EnsureStaticMembers() 
    { 
     if (s_doc == null || s_nsmgr == null) 
     { 
      lock (s_lock) 
      { 
       if (s_doc == null || s_nsmgr == null) 
       { 
        // The following method might fail 
        // with an exception, but if it succeeds, 
        // s_doc and s_nsmgr will be initialized 
        s_doc = LoadDoc(out s_nsmgr); 
       } 
      } 
     } 
    } 

    public XmlNamespaceManager NamespaceManager 
    { 
     get 
     { 
      EnsureStaticMembers(); 
      return s_nsmgr; 
     } 
    } 

    public XmlDocument GetDocClone() 
    { 
     EnsureStaticMembers(); 
     return (XmlDocument)s_doc.Clone(); 
    } 
} 
+0

Problèmes de verrouillage par double vérification dans .NET? Citation? –

+0

C'était l'article de Jon Skeets qui ressortait le plus, mais j'ai vu beaucoup d'articles et de discussions ici et là disant que personne ne sait vraiment si c'est sûr sur C# et/ou de ne pas l'utiliser entièrement. Je n'ai encore vu personne en fait recommander l'utilisation sur .NET. – Jimmy

+1

Je pense que Jon a fait une analyse très décente. Cela fonctionne mais des mots comme «modèle de mémoire» en combinaison avec «version de plate-forme» devraient vous effrayer. –

Répondre

2

Si vous utilisez .NET 4.0 vous pouvez consulter Lazy<T> et LazyThreadSafetyMode (cela dépend si vous voulez quelques exemples de T à créer ou non dans un environnement multithread. Vous devez vous référer Dans votre cas à Lazy<T>(Func<T> func, LazyThreadSafetyMode) constructeur - here(MSDN)

Sinon (si vous utilisez 3.5 ou moins), vous pouvez créer une instance unique sans verrou.

Quelque chose comme ceci:

get { 
    if(_instance == null) { 
     var singleton = new Singleton(); 
     if(Interlocked.CompareExchange(ref _instance, singleton, null) != null) { 
      if (singleton is IDisposable) singleton.Dispose(); 
     } 
    } 
    return _instance; 
} 

Cependant, ici vous ne pouvez obtenir un comportement de LazyThreadSafetyMode.Publications - une seule instance sera visible aux autres threads, mais quelques-uns peuvent être créés.

En outre, il ne devrait pas y avoir de problèmes avec la vérification de null dans votre code - c'est sûr dans le monde .NET (au moins sur les machines x86 et le modèle de mémoire associé). Il y avait quelques problèmes dans le monde de Java avant 2004, AFAIK.

+0

Merci de votre contribution. L'approche lock() semble plus simple et mieux adaptée puisque je travaille avec deux variables à initialiser, mais je vais certainement envisager d'utiliser Interlocked.CompareExchange() pour les problèmes futurs. Je n'utilise pas .NET 4.0, donc Lazy n'est pas une option pour le moment. – Jimmy

Questions connexes