Domanda

Il mio team sta attualmente discutendo questo problema.

Il codice in questione è qualcosa per le linee di

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

Alcuni dei post che ho visto dicono che questo potrebbe essere un grosso no no (quando si utilizza TryGetValue).Eppure i membri del nostro team dicono che è ok poiché "Containskey" non interaceo sulla collezione Key ma controlla se la chiave è contenuta tramite il codice hash in O (1).Quindi affermano che non c'è pericolo qui.

vorrei ottenere le tue opinioni oneste riguardo a questo problema.

È stato utile?

Soluzione

Non farlo.Non è sicuro.

Si potrebbe chiamare ContainsKey da un thread mentre un altro thread chiama Add.Questo è semplicemente supportato da Dictionary<TKey, TValue>.Se Add ha bisogno di riallocare secchi ecc., Posso immaginarli potrebbe ottenere alcuni risultati molto strani o un'eccezione.IT May è stato scritto in modo tale da non vedere effetti cattivi, ma non mi piacerebbe fare affidamento su di esso.

È una cosa che utilizza il blocco a doppio controllo per semplici letture / scritture su un campo, anche se avrei comunque discusso contro di esso - è un altro per effettuare chiamate a un'API che è stata esplicitamente descritta come non Essere al sicuro per più chiamate simultanee.

Se sei su .net 4, ConcurrentDictionary è probabilmentela strada davanti.Altrimenti, basta bloccare ogni accesso.

Altri suggerimenti

Se sei in un ambiente multithreading, potresti preferire guardare usando un concordentDictionary.Ho bloggato un paio di mesi fa, potresti trovare l'articolo utile: http://colinmackay.co.uk/blog/2011/03/24/pallelization-in-net-4-0-the-concurrent-dictionary/

Questo codice non è corretto.Il tipo Dictionary<TKey, TValue> non supporta operazioni di lettura e scrittura simultanee.Anche se il tuo metodo Add è chiamato all'interno del blocco, il ContainsKey non lo è.Quindi permette facilmente una violazione della regola di lettura / scrittura simultanea e porterà alla corruzione nell'istanza

Non sembra cassaforte con filo, ma probabilmente sarebbe difficile farlo fallire.

L'argomento Lookup di Iteration vs Hash non tiene, potrebbe esserci una collisione di hash ad esempio.

If this dictionary is rarely written and often read, then I often employ safe double locking by replacing the entire dictionary on write. This is particularly effective if you can batch writes together to make them less frequent.

For example, this is a cut down version of a method we use that tries to get a schema object associated with a type, and if it can't, then it goes ahead and creates schema objects for all the types it finds in the same assembly as the specified type to minimize the number of times the entire dictionary has to be copied:

    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];
        }
    }

So the dictionary in this case will be rebuilt, at most, as many times as there are assemblies with types that need schemas. For the rest of the application lifetime the dictionary accesses will be lock-free. The dictionary copy becomes a one-time initialization cost of the assembly. The dictionary swap is thread-safe because pointer writes are atomic so the whole reference gets switched at once.

You can apply similar principles in other situations as well.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top