Es casi seguro que intentar sincronizar internamente será insuficiente porque tiene un nivel de abstracción demasiado bajo. Supongamos que hace que las operaciones Add
y ContainsKey
sean seguras para subprocesos individualmente de la siguiente manera:
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);
}
}
Entonces, ¿qué sucede cuando llamas a este bit de código supuestamente seguro para subprocesos desde varios subprocesos? ¿Siempre funcionará bien?
if (!mySafeDictionary.ContainsKey(someKey))
{
mySafeDictionary.Add(someKey, someValue);
}
La respuesta simple es no. En algún momento, el Add
método lanzará una excepción que indica que la clave ya existe en el diccionario. ¿Cómo puede ser esto con un diccionario seguro para subprocesos? Bueno, solo porque cada operación es segura para subprocesos, la combinación de dos operaciones no lo es, ya que otro subproceso podría modificarlo entre su llamada a ContainsKey
y Add
.
Lo que significa que para escribir este tipo de escenario correctamente, necesita un candado fuera del diccionario, por ejemplo
lock (mySafeDictionary)
{
if (!mySafeDictionary.ContainsKey(someKey))
{
mySafeDictionary.Add(someKey, someValue);
}
}
Pero ahora, dado que tiene que escribir código de bloqueo externo, está mezclando la sincronización interna y externa, lo que siempre conduce a problemas como código poco claro y puntos muertos. Entonces, en última instancia, probablemente sea mejor para:
Use un normal Dictionary<TKey, TValue>
y sincronice externamente, encerrando las operaciones compuestas en él, o
Escriba un nuevo contenedor seguro para subprocesos con una interfaz diferente (es decir, no IDictionary<T>
) que combine las operaciones, como un AddIfNotContained
método, para que nunca tenga que combinar operaciones desde él.
(Tiendo a ir con el # 1 yo mismo)