2009-04-27 22 views
2

Est-ce threadsafe de la classe ValueStore? La portée du verrou dans GetInt (clé de chaîne) doit-elle être étendue autour du rendement du rendement?Est-ce que cette classe est threadsafe?

public class ValueStore 
{ 
    private readonly object _locker = new object(); 
    private readonly Dictionary<string, int> _data = 
    new Dictionary<string, int>(); 

    public ValueStore(Dictionary<string, int> data) 
    { 
    _data = data; 
    } 

    public IEnumerable<int> GetInt(string key) 
    { 
    IEnumerable<KeyValuePair<string, int>> selected; 
    lock(_locker) 
    { 
     selected = _data.Where(x => x.Key.Equals(key)); 
    } 

    foreach (KeyValuePair<string, int> pair in selected) 
    { 
     yield return pair.Value; 
    } 
    } 
} 

Le test unitaire semble être bien:

[TestFixture] 
public class ValueStoreTest 
{ 
    [Test] 
    public void test1() 
    { 
    Dictionary<string, int> data = new Dictionary<string, int>(); 
    for (int i = 0; i < 100000; i++) 
    { 
     data.Add(i.ToString(),i); 
    } 

    ValueStore vs = new ValueStore(data); 

    for (int i = 0; i < 900000; i++) 
    { 
     ThreadPool.QueueUserWorkItem(delegate 
     { 
     for (int j = 0; j < 100000; j++) 
     { 
      IEnumerable<int> d = vs.GetInt(j.ToString()); 
     } 
     }); 
    } 
    } 
} 
+0

Vous avez ici des problèmes plus sérieux que la sécurité des threads. Vous utilisez le dictionnaire incorrect. Vous l'utilisez comme une liste. - Vous semblez pouvoir affecter plusieurs valeurs à une clé. Tu ne peux pas. Donc, le rendement est inutile. - Vous enumerez toutes les valeurs dans le dictionnaire au lieu de simplement utiliser les méthodes contains/get. –

Répondre

6

Non, il est certainement pas thread-safe. Le fait qu'il utilise un dictionnaire transmis par le client signifie que vous n'avez aucun contrôle sur le moment où le client le change. Vous ne le verrouillez également que lorsque vous appliquez la clause Where - mais cela n'effectue aucune itération. Vous aurez besoin de maintenir le verrou pendant l'itération sur les résultats - mais comme je l'ai déjà dit, cela n'empêche pas le client de changer le dictionnaire à tout moment de toute façon.

Si vous avez créé le dictionnaire dans la classe et que vous n'avez jamais exposé les données qu'il contient (c'est-à-dire que vous l'avez protégé du monde extérieur), vous pouvez le rendre totalement sécurisé. Si vous insistez sur le fait que le code client ne mute pas le dictionnaire, vous n'avez absolument pas besoin du verrou, car les dictionnaires peuvent être lus en toute sécurité à partir de plusieurs threads lorsqu'il n'y a pas de script.

1

Je peux vous dire que l'instruction dans le verrou n'est pas exécutée avant que le verrou ne soit libéré. Si vous devez verrouiller la collection pendant l'itération, déplacez le rendement dans l'instruction lock.

+2

L'instruction dans le verrou * est * exécutée dans le verrou. C'est juste que cette instruction renvoie un itérateur d'exécution différée. L'expression * lambda * n'est pas exécutée dans le verrou, si c'est ce que vous vouliez dire. –

1

Non, ce n'est pas le cas. Si vous commencez à lire et à écrire dans l'objet Dictionary<string, int> que vous avez transmis au constructeur, vous avez un problème. Votre déclaration de classe de _data est immédiatement écrasée par l'affectation dans le constructeur.

Pour corriger cela, copiez chaque paire clé/valeur de la transmise dans Dictionary dans le constructeur dans votre classe Dictionary plutôt que l'affectation directe. Ensuite, je pense que vous êtes thread-safe, mais évidemment votre classe est en lecture seule.

Questions connexes