Der Versuch, intern zu synchronisieren, wird mit ziemlicher Sicherheit unzureichend sein, da die Abstraktionsebene zu niedrig ist. Angenommen, Sie machen die Operationen Add
und ContainsKey
wie folgt einzeln threadsicher:
public void Add(TKey key, TValue value)
{
lock (this.syncRoot)
{
this.innerDictionary.Add(key, value);
}
}
public bool ContainsKey(TKey key)
{
lock (this.syncRoot)
{
return this.innerDictionary.ContainsKey(key);
}
}
Was passiert dann, wenn Sie dieses vermeintlich threadsichere Codebit aus mehreren Threads aufrufen? Wird es immer gut funktionieren?
if (!mySafeDictionary.ContainsKey(someKey))
{
mySafeDictionary.Add(someKey, someValue);
}
Die einfache Antwort lautet nein. Irgendwann löst die Add
Methode eine Ausnahme aus, die angibt, dass der Schlüssel bereits im Wörterbuch vorhanden ist. Wie kann das mit einem thread-sicheren Wörterbuch sein, könnte man fragen? Nur weil jede Operation threadsicher ist, ist die Kombination von zwei Operationen nicht möglich, da ein anderer Thread sie zwischen Ihrem Aufruf von ContainsKey
und ändern könnte Add
.
Um diese Art von Szenario korrekt zu schreiben, benötigen Sie eine Sperre außerhalb des Wörterbuchs, z
lock (mySafeDictionary)
{
if (!mySafeDictionary.ContainsKey(someKey))
{
mySafeDictionary.Add(someKey, someValue);
}
}
Da Sie jedoch extern sperrenden Code schreiben müssen, verwechseln Sie die interne und externe Synchronisation, was immer zu Problemen wie unklarem Code und Deadlocks führt. Letztendlich sind Sie wahrscheinlich entweder besser:
Verwenden Sie eine normale Dictionary<TKey, TValue>
und synchronisieren Sie extern, indem Sie die zusammengesetzten Operationen darauf einschließen, oder
Schreiben Sie einen neuen thread-sicheren Wrapper mit einer anderen Schnittstelle (dh nicht IDictionary<T>
), die die Operationen wie eine AddIfNotContained
Methode kombiniert , sodass Sie niemals Operationen daraus kombinieren müssen.
(Ich neige dazu, selbst mit # 1 zu gehen)