Frage

Mein Team diskutiert derzeit dieses Thema.

Der fragliche Code ist etwas in der Art von

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

Einige der Beiträge, die ich gesehen habe, besagen, dass dies ein großes NEIN sein kann (bei Verwendung von TryGetValue ).Mitglieder unseres Teams sagen jedoch, dass dies in Ordnung ist, da "ContainsKey" die Schlüsselsammlung nicht iteriert, sondern über den Hash-Code in O (1) prüft, ob der Schlüssel enthalten ist.Daher behaupten sie, dass hier keine Gefahr besteht.

Ich würde gerne Ihre ehrliche Meinung zu diesem Thema erfahren.

War es hilfreich?

Lösung

Tu das nicht.Es ist nicht sicher.

Du könntest anrufen ContainsKey von einem Thread, während ein anderer Thread aufruft Add.Das wird einfach nicht unterstützt von Dictionary<TKey, TValue>.Wenn Add muss Eimer usw. neu zuweisen, ich kann mir vorstellen, dass Sie können erhalten Sie einige sehr seltsame Ergebnisse oder eine Ausnahme.Es können wurden so geschrieben, dass Sie keine unangenehmen Effekte sehen, aber ich möchte mich nicht darauf verlassen.

Es ist eine Sache, doppelt geprüftes Sperren für einfache Lese- / Schreibvorgänge in ein Feld zu verwenden, obwohl ich immer noch dagegen argumentieren würde - es ist eine andere Sache, Aufrufe an eine API zu tätigen, die explizit als beschrieben wurde nicht sicher für mehrere gleichzeitige Anrufe.

Wenn Sie auf .NET 4 sind, ConcurrentDictionary ist wahrscheinlich der Weg nach vorne.Andernfalls sperren Sie einfach jeden Zugriff.

Andere Tipps

Wenn Sie sich in einer Multithread-Umgebung befinden, möchten Sie es lieber mit einer Konzernzählung anzusehen.Ich habe vor ein paar Monaten noch einmal bloggt, Sie könnten den Artikel nützlich finden: http://colinmackay.co.uk/blog/2011/03/24/paralliSiSt-in-net-4-0-the-concurrent-dictionary/

This code is incorrect. The Dictionary<TKey, TValue> type does not support simultaneous read and write operations. Even though your Add method is called within the lock the ContainsKey is not. Hence it easily allows for a violation of the simultaneous read / write rule and will lead to corruption in your instance

It doesn't look thread-safe, but it would probably be hard to make it fail.

The iteration vs hash lookup argument doesn't hold, there could be a hash-collision for instance.

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top