2009-08-25 9 views
2

Je suis très nouveau dans le multi-threading et pour une raison quelconque, cette classe me pose plus de problèmes qu'elle ne le devrait.Dictionnaire C# avec ReaderWriterLockSlim

Je suis en train de configurer un dictionnaire dans le cache ASP.net - Il sera fréquemment interrogé pour des objets individuels, énumérés occasionnellement, et écrits extrêmement rarement. Je noterai que les données du dictionnaire ne sont presque jamais changées, je prévois de le laisser expirer quotidiennement avec un rappel pour reconstruire à partir de la base de données quand il quitte le cache. Je crois que l'énumération et l'accès par les clés sont sûrs tant que le dictionnaire n'est pas écrit. Je pense qu'une classe wrapper basée sur ReaderWriterLockSlim est la voie à suivre mais je suis floue sur quelques points. Si j'utilise Lock, je crois que je peux soit verrouiller sur un jeton soit sur l'objet que je suis en train de protéger. Je ne vois pas comment faire quelque chose de similaire en utilisant le ReaderWriter Lock. Ai-je raison de penser que plusieurs instances de mon wrapper ne se verrouilleront pas correctement, étant donné que les ReaderWriterLocks sont hors de portée les unes des autres?

Quelle est la meilleure pratique pour écrire un emballage comme celui-ci? Construire en tant que statique semble presque redondant que l'objet principal est maintenu par le cache. Singleton semblent être désapprouvés, et je suis préoccupé par les questions de cadrage mentionnées ci-dessus pour les instances individuelles.

J'ai vu quelques implémentations de wrappers similaires mais je n'ai pas été capable de répondre à ces questions. Je veux juste m'assurer que j'ai une bonne compréhension de ce que je fais plutôt que de couper & en passant mon chemin. Merci beaucoup pour votre aide!


** Edit: Espérons que cela est un résumé plus claire de ce que je suis en train de trouver OUT- **

1. Ai-je raison de penser que la serrure ne modifie pas les données sous-jacentes et est défini comme toute autre variable?

À titre d'exemple permet de dire que je donne les résultats suivants -

MyWrapperClass 
{ 
    ReaderWriterLockSlim lck = new ReaderWriterLockSlim(); 
    Do stuff with this lock on the underlying cached dictionary object... 
} 

MyWrapperClass wrapA = new MyWrapperClass(); 
MyWrapperClass wrapB = new MyWrapperClass(); 

Ai-je raison de penser que le verrou WRAPA et verrouillage wrapB ne réagissent pas, et que si WRAPA & wrapB les deux opérations de tentative il sera être dangereux?

2. Si tel est le cas, quelle est la meilleure façon de "partager" les données de verrouillage?

Ceci est une application Asp.net - il y aura plusieurs pages qui ont besoin d'accéder aux données, c'est pourquoi je le fais en premier lieu. Quelle est la meilleure pratique pour s'assurer que les différents emballages utilisent la même serrure? Mon wrapper doit-il être un static ou un singleton que tous les threads utilisent, sinon quelle est l'alternative la plus élégante?

Répondre

6

Vous avez plusieurs objets de dictionnaire dans le cache, et vous souhaitez que chacun soit verrouillé de manière indépendante. Le "meilleur" moyen est de simplement utiliser une classe simple qui le fait pour vous.

public class ReadWriteDictionary<K,V> 
{ 
    private readonly Dictionary<K,V> dict = new Dictionary<K,V>(); 
    private readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(); 

    public V Get(K key) 
    { 
     return ReadLock(() => dict[key]); 
    } 

    public void Set(K key, V value) 
    { 
     WriteLock(() => dict.Add(key, value)); 
    } 

    public IEnumerable<KeyValuePair<K, V>> GetPairs() 
    { 
     return ReadLock(() => dict.ToList()); 
    } 

    private V2 ReadLock<V2>(Func<V2> func) 
    { 
     rwLock.EnterReadLock(); 
     try 
     { 
      return func(); 
     } 
     finally 
     { 
      rwLock.ExitReadLock(); 
     } 
    } 

    private void WriteLock(Action action) 
    { 
     rwLock.EnterWriteLock(); 
     try 
     { 
      action(); 
     } 
     finally 
     { 
      rwLock.ExitWriteLock(); 
     } 
    } 
} 

Cache["somekey"] = new ReadWriteDictionary<string,int>(); 

Il y a aussi un exemple plus complet sur la page d'aide de ReaderWriterLockSlim on MSDN. Ce ne serait pas difficile de le rendre générique.

modifier Pour répondre à vos questions nouvelles -

1.) Vous êtes WRAPA correct et wrapB n'interagir. Ils ont tous deux leur propre instance de ReaderWriterLockSlim. 2. Si vous avez besoin d'un verrou partagé parmi toutes vos classes wrapper, alors il doit être statique.

+0

Je ne faisais que retravailler la question que vous avez postée. Cela contourne complètement complètement les problèmes qui me préoccupaient. Je pensais que je mettrais le dictionnaire en cache en interne et que le wrapper serait externe, mais votre solution semble définitivement plus élégante. Merci! –

+0

Parfait- Merci beaucoup! La mise en cache de l'ensemble de l'objet par opposition au dictionnaire semble être une bien meilleure approche. J'apprécie vraiment la clarification, j'avais peur d'aller de l'avant sans être sûr de ce que je faisais et je n'arrivais pas à trouver un exemple solide qui soit confirmé d'une façon ou d'une autre. Désolé de changer la question sur vous pendant que vous étiez en train de poster! Encore une fois j'apprécie toute l'aide qui a rendu la chose beaucoup plus claire! –

+1

ATTENTION !!! . L'énumération d'un dictionnaire n'est pas sûre pour les threads, même en le lisant. Il devrait y avoir un verrou en écriture avec GetPairs() non lu lock. Donc, le code ci-dessus pourrait échouer comme il est maintenant – user67754

-1

La manière standard de verrouiller est: object lck = new object(); ... lock(lck) { ... } dans ce cas l'objet lck représente le verrou.

ReadWriterLockSlim n'est pas très différent, c'est juste dans ce cas la classe ReadWriterLockSlim réelle représente le verrou réel, donc partout où vous auriez utilisé lck vous utilisez maintenant votre ReadWriterLockSlim.

ReadWriterLockSlim lck = new ReadWriterLockSlim(); 

... 

lck.EnterReadLock(); 
try 
{ 
    ... 
} 
finally 
{ 
    lck.ExitReadLock(); 
} 
+0

C'est ainsi que j'ai configuré le verrou maintenant, mais si mon objet wrapper a plusieurs instances, je crains que les objets lck ne soient pas dans la même portée et n'interagissent donc pas comme prévu . Je pourrais bien sûr faire du lck une variable statique, ou rendre statique toute ma classe wrapper mais je veux voir si c'est nécessaire, et corriger pour le faire. Merci! –

+1

Il n'y a pas non plus un bon plan, assurez-vous que vous n'avez qu'une seule classe wrapper. En fait, arrêtez de le considérer comme une classe de wrapper et pensez-y comme la classe réelle, le fait qu'il utilise un dictionnaire étant un détail d'implémentation de cette classe. À ce stade, vous ne devriez pas révéler le dictionnaire en dehors de cette classe. –

0

ConcurrentDictionary fait tout ce que vous voulez, puis certains. Une partie de System.Concurrent.Collections

+0

Merci! Malheureusement, ça n'existait pas quand je regardais ça avant. C'est génial de l'avoir maintenant. –

Questions connexes