Question

Mon équipe débat actuellement de cette question.

Le code en question est quelque chose dans le sens de

if (!myDictionary.ContainsKey(key))
{
    lock (_SyncObject)
    {
        if (!myDictionary.ContainsKey(key))
        {
            myDictionary.Add(key,value);
        }
    }
}

Certains des messages que j'ai vus ont dit que cela peut être un grand non non (lorsque vous utilisez TryGetValue). Pourtant, les membres de notre équipe disent que c'est OK car "ContintainsKey" n'imagine pas la collection de clés mais vérifie si la clé est contenue via le code de hachage en O (1). Par conséquent, ils affirment qu'il n'y a pas de danger ici.

Je voudrais obtenir vos opinions honnêtes sur ce problème.

Était-ce utile?

La solution

Ne fais pas ça. Ce n'est pas prudent.

Tu pourrais appeler ContainsKey à partir d'un thread tandis qu'un autre thread appelle Add. Ce n'est tout simplement pas soutenu par Dictionary<TKey, TValue>. Si Add besoin de réaffecter les seaux, etc., je peux vous imaginer pourrait Obtenez des résultats très étranges ou une exception. Ce peut ont été écrits de telle manière que vous ne voyez aucun effet désagréable, mais je ne voudrais pas en compter.

C'est une chose en utilisant un verrouillage à double vérification pour des lectures / écritures simples dans un domaine, bien que je me dispute toujours - c'est un autre de passer des appels à une API qui a été explicitement décrite comme ne pas être sûr pour plusieurs appels simultanés.

Si vous êtes sur .net 4, ConcurrentDictionary est probablement la voie à suivre. Sinon, verrouillez simplement chaque accès.

Autres conseils

Si vous êtes dans un environnement multithread, vous préférez peut-être envisager d'utiliser undictionnaire concurrent. J'ai blogué à ce sujet il y a quelques mois, vous pourriez trouver l'article utile: http://colinmackay.co.uk/blog/2011/03/24/Parallelisation-in-Net-4-0-the-Concurrent-dictionary/

Ce code est incorrect. La Dictionary<TKey, TValue> Le type ne prend pas en charge les opérations de lecture et d'écriture simultanées. Même si votre Add la méthode est appelée dans le verrouillage le ContainsKey n'est pas. Par conséquent, il permet facilement une violation de la règle de lecture / écriture simultanée et entraînera une corruption dans votre cas

Il n'a pas l'air en forme de fil, mais il serait probablement difficile de le faire échouer.

L'argument de recherche d'itération contre le hachage ne tient pas, il pourrait y avoir une collision de hachage par exemple.

Si ce dictionnaire est rarement écrit et souvent lu, alors j'emploie souvent un verrouillage double en remplaçant l'ensemble du dictionnaire en écriture. Ceci est particulièrement efficace si vous pouvez les écrire ensemble pour les rendre moins fréquents.

Par exemple, il s'agit d'une version coupée d'une méthode que nous utilisons qui essaie d'obtenir un objet de schéma associé à un type, et s'il ne le peut pas, alors il va de l'avant et crée des objets de schéma pour tous les types qu'il trouve dans le même assemblage comme type spécifié pour minimiser le nombre de fois où l'ensemble du dictionnaire doit être copié:

    public static Schema GetSchema(Type type)
    {
        if (_schemaLookup.TryGetValue(type, out Schema schema))
            return schema;

        lock (_syncRoot) {
            if (_schemaLookup.TryGetValue(type, out schema))
                return schema;

            var newLookup = new Dictionary<Type, Schema>(_schemaLookup);

            foreach (var t in type.Assembly.GetTypes()) {
                var newSchema = new Schema(t);
                newLookup.Add(t, newSchema);
            }

            _schemaLookup = newLookup;

            return _schemaLookup[type];
        }
    }

Ainsi, le dictionnaire dans ce cas sera reconstruit, au plus, autant de fois qu'il existe des assemblées avec des types qui nécessitent des schémas. Pour le reste de la durée de vie de l'application, les accès du dictionnaire seront sans verrouillage. La copie du dictionnaire devient un coût d'initialisation unique de l'assemblage. L'échange de dictionnaire est fileté car les écritures de pointeur sont atomiques, donc toute la référence est changée en même temps.

Vous pouvez également appliquer des principes similaires dans d'autres situations.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top