2009-09-17 4 views
2

Mise de côté, pour l'instant, des arguments concernant les vertus et disvirtues relatives du motif Singleton, et considérant qu'un Singleton est généralement considéré comme une instance qui persiste pendant la durée de vie d'une application, Quelle serait la meilleure façon d'avoir un Singleton qui a une vie limitée?Expiration de l'instance Singleton après une période de temps

Y at-il quelque chose de mal avec quelque chose comme ce qui suit:

public class CategoryHandler 
{  

    private static DateTime m_expires; 

    public bool HasExpired 
    { 
     get return DateTime.Now > m_expires; 
    } 

    private CategoryHandler() 
    { 
     m_expires = DateTime.Now.AddMinutes(60); 
    } 

    public static CategoryHandler Instance() 
    { 
     if(HasExpired) 
     { 
      //Dispose and reconstruct 
     } 
     else 
     { 
      //Use existing instance 
     } 
    } 

} 

Ou est-il un moyen beaucoup mieux d'aborder ce problème?

+3

Avez-vous besoin d'expirer le singleton en le disposant après 60 minutes, ou juste "la prochaine fois que quelqu'un l'utilisera après 60 minutes"? –

+0

Ce serait la prochaine fois qu'il est utilisé après 60 minutes (ou si longtemps, j'ai choisi 60 minutes juste pour l'exemple). – Jason

Répondre

3

Vous aurez besoin d'une sorte de verrouillage pour assurer la sécurité des threads:

public sealed class CategoryHandler 
{ 
    private static CategoryHandler _instance = null; 
    private static DateTime _expiry = DateTime.MinValue; 
    private static readonly object _lock = new object(); 

    private CategoryHandler() { } 

    public static bool HasExpired 
    { 
     get 
     { 
      lock (_lock) { return (_expiry < DateTime.Now); } 
     } 
    } 

    public static CategoryHandler Instance 
    { 
     get 
     { 
      lock (_lock) 
      { 
       if (HasExpired) 
       { 
        // dispose and reconstruct _instance 
        _expiry = DateTime.Now.AddMinutes(60); 
       } 
       return _instance; 
      } 
     } 
    } 
} 
+0

En effet, je le connais, je le connais. Je pense que j'ai peut-être trop simplifié le code de l'exmaple! – Jason

2

Le seul problème majeur que je peux voir est qu'une autre classe a déjà une référence à l'ancienne instance, cela ne l'invalidera pas. Donc, tant que toutes les classes font:

CategoryHandler.Instance.Method(); 

plutôt que

CategoryHandler singleton = CategoryHandler.Instance; 
... 
singleton.SomeMethod(); 

vous devriez bien à condition que vous êtes heureux que le singleton prendra fin la prochaine fois qu'il est fait référence plutôt que après exactement soixante minutes .

Si vous avez besoin qu'il expire après une période de temps précise, alors vous devrez utiliser une minuterie et remplacer l'instance dans la méthode de rappel (attention le minuteur rappellera dans un thread différent alors assurez-vous d'implémenter un modèle de singleton sûr de filetage)

0

Que voulez-vous exactement accomplir avec un singleton expirant? Je pense que vous avez un problème avec votre conception si vous voulez vraiment faire cela. Et comme Martin l'a dit, vous aurez des ennuis si du code "capture" votre instance singleton.

Alors, quelle est exactement votre utilisation pour cela? Ou est la question juste par curiosité?

+0

Actuellement, la classe CategoryHandler singleton possède une liste qui contient une liste de catégories chargées à partir d'un fichier xml la première fois que l'instance est demandée. On m'a demandé de changer ceci pour obtenir les catégories d'une base de données, et pour que la liste mette à jour immédiatement sur chaque serveur Web qui exécute l'application. Clairement, je peux forcer la destruction du singleton sur le même serveur qui met à jour la base de données mais je n'ai aucun moyen de détruire l'instance sur les autres serveurs. Cette idée d'expiration Singleton est l'une des possibilités que je considère. – Jason

+0

@Jibberish: Si vous utilisez ASP.NET alors pourquoi ne pas utiliser le 'Cache' intégré pour cela? – LukeH

+0

Luke: c'est une autre option que j'ai et merci pour la suggestion. – Jason

1

Pour éviter d'avoir le problème avec des classes ayant des références à une ancienne instance que vous pourriez envisager d'écrire un wrapper de mise en cache pour votre classe. extrait du poing le ICategoryHandler d'interface, permet de supposer qu'il ressemble à ceci:

interface ICategoryHandler 
{ 
    int A(); 
} 

et vous mettre en œuvre l'enveloppe (verrouillage omis):

class CategoryHandlerWrapper : ICategoryHandler 
{ 
    ICategoryHandler instance; 
    private DateTime expiry = DateTime.MinValue; 

    public int A() 
    { 
    return Instance().A(); 
    } 

    public bool HasExpired 
    { 
     get return DateTime.Now > expiry; 
    } 

    private CategoryHandler Instance() 
    { 
     if(HasExpired) 
     { 
      //Dispose and reconstruct 
     } 
     else 
     { 
      //Use existing instance 
     } 
    } 
} 

De cette façon, vous pouvez utiliser l'injection de dépendance normale et toujours profiter de la caractéristique que vous êtes après. Aussi le problème avec les anciennes références est encapsulé dans un endroit.

1

Avez-vous envisagé d'utiliser un IoC/DI Container framework et de gérer la durée de vie du "singleton"?

La version Unity de Microsoft est celle que je connais le mieux, et elle ne fournit pas un LifetimeManager prêt à l'emploi qui fasse exactement ce que vous voulez faire. Cependant, vous pouvez créer votre propre descendant; voir Writing Custom Lifetime Managers.

Ou vous pouvez regarder les autres cadres et voir si l'on a ce que vous voulez dans la boîte.

EDIT: Notez que cela ne vous sortira pas du problème "capturé" mentionné par Martin.

Questions connexes