.net - net - Thread-safe Dictionary.Add
thread safe dictionary c# (2)
El diccionario no es seguro para subprocesos, independientemente de si solo lo agrega o no: hay algunas estructuras internas que deben mantenerse sincronizadas (especialmente cuando los hashbuckets internos se redimensionan).
O bien tiene que implementar su propio bloqueo en cualquier operación, o si está en .Net 4.0 puede usar el nuevo ConcurrentDictionary, que es absolutamente fantástico, y que es totalmente seguro para subprocesos.
Otra opción (actualización)
Dicho esto, hay otra técnica que puedes usar, pero requerirá un poco de ajuste dependiendo del tipo de datos que estés insertando en tu diccionario, y de si todas tus claves tienen garantía única:
Dele a cada hilo su propio diccionario privado en el que se inserta.
Cuando termine cada tema, coteje todos los diccionarios y combínelos en uno más grande; cómo manejas las llaves duplicadas depende de ti. Por ejemplo, si almacena en caché listas de elementos con una clave, puede simplemente fusionar cada lista con la misma clave en una y colocarla en el diccionario maestro.
Respuesta oficial re: rendimiento (después de haber aceptado)
Entonces, como dicen sus comentarios, necesita una idea del mejor método (bloqueo o combinación) para el rendimiento, etc. No puedo decirle qué será; en última instancia, tendrá que ser referenciada. Pero veré si puedo ofrecer algo de guía :)
En primer lugar, si tiene alguna idea de cuántos elementos necesitará su Dictionar (y / ies), use el constructor (int)
para minimizar el cambio de tamaño.
La operación de fusión es probable que sea mejor; ya que ninguno de los hilos interferirá entre sí. A menos que el proceso involucrado cuando dos objetos comparten la misma clave sea particularmente largo; en cuyo caso forzarlo a que ocurra en un solo hilo al final de la operación podría terminar poniendo a cero todas las ganancias de rendimiento al paralelizar la primera etapa.
Igualmente, existe la posibilidad de que exista memoria, ya que efectivamente clonará el diccionario, por lo que si el resultado final es lo suficientemente grande, podría terminar consumiendo muchos recursos; concedido, sin embargo, serán liberados.
Si es el caso de que deba tomarse una decisión al nivel del subproceso cuando una clave ya está presente, entonces necesitará una construcción lock () {}.
En un diccionario, esto generalmente toma la siguiente forma:
readonly object locker = new object();
Dictionary<string, IFoo> dictionary = new Dictionary<string, IFoo>();
void threadfunc()
{
while(work_to_do)
{
//get the object outside the lock
//be optimistic - expect to add; and handle the clash as a
//special case
IFoo nextObj = GetNextObject(); //let''s say that an IFoo has a .Name
IFoo existing = null;
lock(locker)
{
//TryGetValue is a god-send for this kind of stuff
if(!dictionary.TryGetValue(nextObj.Name, out existing))
dictionary[nextObject.Name] = nextObj;
else
MergeOperation(existing, nextObject);
}
}
}
Ahora si MergeOperation
es realmente lento; entonces podría considerar liberar el bloqueo, crear un objeto clonado que represente la fusión del objeto existente y el nuevo, y luego volver a adquirir el bloqueo. Sin embargo, necesita una manera confiable de verificar que el estado del objeto existente no haya cambiado entre el primer bloqueo y el segundo (un número de versión es útil para esto).
¿Es seguro el hilo Dictionary.Add()
cuando solo se inserta?
Tengo un código que inserta claves de varios hilos, ¿todavía necesito bloquearlo en Dictionary.Add ()?
Obtuve esta excepción al agregar una nueva clave:
Exception Source: mscorlib
Exception Type: System.IndexOutOfRangeException
Exception Message: Index was outside the bounds of the array.
Exception Target Site: Insert
Aunque es bastante raro. Sé que Dictionary
no es seguro para subprocesos aunque pensé que solo llamar a .Add
no causaría ningún problema.
Sí, esta es la excepción que puede obtener cuando inserta un elemento de la misma manera que el diccionario está ocupado aumentando el número de segmentos. Disparado por otro hilo que agrega un elemento y el factor de carga es demasiado alto. El diccionario es especialmente sensible a eso porque la reorganización lleva un tiempo. Lo bueno es que hace que su código se bloquee rápidamente en lugar de solo una vez a la semana.
Revise cada línea de código que se usa en un hilo y verifique dónde se utiliza un objeto compartido. Todavía no has encontrado los bloqueos de una vez a la semana. O peor, los que no se bloquean sino que generan datos incorrectos de vez en cuando.