Il y a cet objet semblable à une connexion DB dont mon application Web aura besoin. Il est assez lent à créer et sera rarement utilisé, donc je n'en garderai qu'une seule instance. Si plusieurs demandes en ont besoin en même temps, ils vont lock()
l'objet et sérialiser leur accès.Comment créer une instance unique thread-safe d'un IDisposable?
Pour rendre les choses plus amusantes, l'objet est IDisposable et peut être fermé. Naturellement, j'éviterai d'écrire du code qui le ferme, mais ... tu sais ... mieux vaut prévenir que guérir, non?
Ainsi, l'approche naïve serait de mettre en œuvre quelque chose comme ceci:
private static DBClass _Instance;
private static object _DBLock = new object();
public DBClass GetDB()
{
if (_Instance == null)
lock (_DBLock)
if (_Instance == null)
_Instance = CreateInstance();
lock (_Instance)
if (_Instance.Disposed)
_Instance = CreateInstance();
return _Instance;
}
Il sera ensuite utilisé comme:
lock (var Conn = Global.GetDB())
{
// Use Conn here.
}
Cela fonctionnera probablement la plupart du temps, mais je voir une ouverture qui pourrait être exploitée - si deux threads l'appellent en même temps, ils obtiennent la même instance en même temps, et alors on pourrait encore l'Dispose()
avant que l'autre n'obtienne un lock()
dessus. Ainsi - le code plus tard échoue. Vérification de Disposed
à chaque endroit qui l'utilise semble également gênant. En fait, cela semble gênant aussi, mais l'instance n'est pas intrinsèquement thread-safe donc je ne peux rien y faire.
Comment implémenteriez-vous ceci?
Le problème est que cette chose retourne d'autres objets qui contiennent une référence à elle et effectuer des opérations sur elle. À moins d'envelopper quelques centaines de cours, ça ne marchera pas. L'approche d'Action <> est remarquable, mais je me demande si l'encombrement accru du code en vaut la peine. Je veux une approche élégante, mais c'est plutôt le contraire ... –
Btw - J'utilise un verrou() de toute façon. Pourquoi le besoin de volatiles? –
Il est possible qu'en raison du verrou * secondary *, vous soyez en règle - mais étant donné que vous avez deux verrous différents impliqués, il n'est pas du tout clair pour moi que ça soit * correct *. Personnellement, j'utiliserais un seul verrou pour tout - mettre toute la méthode dans une instruction de verrouillage en utilisant '_DBLock'. Les subtilités du modèle de mémoire sont assez délicates. De même, si un autre thread utilise l'objet * without * locking mais dispose, il n'y a aucune garantie que vous le repérerez et que vous créerez une nouvelle instance. Fondamentalement, il se sent comme ce design ouvre la place à beaucoup de bugs subtils. –