Il tentativo di sincronizzarsi internamente sarà quasi certamente insufficiente perché è a un livello di astrazione troppo basso. Supponiamo di rendere le operazioni Adde ContainsKeyindividualmente thread-safe come segue:
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);
}
}
Allora cosa succede quando chiami questo bit di codice presumibilmente thread-safe da più thread? Funzionerà sempre bene?
if (!mySafeDictionary.ContainsKey(someKey))
{
mySafeDictionary.Add(someKey, someValue);
}
La semplice risposta è no. Ad un certo punto il Addmetodo genererà un'eccezione che indica che la chiave esiste già nel dizionario. Come può essere con un dizionario thread-safe, potresti chiedere? Bene, solo perché ogni operazione è thread-safe, la combinazione di due operazioni non lo è, poiché un altro thread potrebbe modificarla tra la chiamata a ContainsKeye Add.
Il che significa che per scrivere correttamente questo tipo di scenario è necessario un blocco esterno al dizionario, ad es
lock (mySafeDictionary)
{
if (!mySafeDictionary.ContainsKey(someKey))
{
mySafeDictionary.Add(someKey, someValue);
}
}
Ma ora, visto che devi scrivere codice di blocco esterno, stai confondendo la sincronizzazione interna ed esterna, il che porta sempre a problemi come codice non chiaro e deadlock. Quindi alla fine probabilmente sei meglio:
Usa una normale Dictionary<TKey, TValue>e sincronizza esternamente, racchiudendo le operazioni composte su di essa, o
Scrivi un nuovo wrapper thread-safe con un'interfaccia diversa (cioè no IDictionary<T>) che combini le operazioni come un AddIfNotContainedmetodo in modo da non dover mai combinare le operazioni da esso.
(Tendo ad andare con # 1 me stesso)