Uso correcto del diccionario concurrente

84

¿Estoy en lo cierto al pensar que este es el uso correcto de un diccionario concurrente?

private ConcurrentDictionary<int,long> myDic = new ConcurrentDictionary<int,long>();

//Main thread at program startup

for(int i = 0; i < 4; i++)
{
  myDic.Add(i, 0);
}

//Seperate threads use this to update a value

myDic[InputID] = newLongValue;

No tengo bloqueos, etc. y solo estoy actualizando el valor en el diccionario, aunque varios subprocesos pueden estar intentando hacer lo mismo.

Jon
fuente
2
Depende, ¿ newLongValuedepende del valor anterior de myDic[InputID]?
Damien_The_Unbeliever
3
debe evitar acceder por la clave directamente myDic[InputID]por condición de carrera. Deberías intentarloGetOrAdd
Olivier Albertini
3
@OlivierAlbertini, no creo que myDic[InputID]cause ningún problema cuando se usa como lvalue. GetOrAddno es un reemplazo correcto ya que agrega solo si el valor no existe. En su lugar, podemos usar AddOrUpdatepara agregar / actualizar el mismo valor en el diccionario.
Jatin Sanghvi

Respuestas:

75

Depende de lo que quieras decir con seguro para subprocesos.

Desde MSDN - Cómo: Agregar y quitar elementos de un ConcurrentDictionary :

ConcurrentDictionary<TKey, TValue>está diseñado para escenarios multiproceso. No tiene que usar candados en su código para agregar o quitar elementos de la colección. Sin embargo, siempre es posible que un hilo recupere un valor y otro hilo actualice inmediatamente la colección dando a la misma clave un nuevo valor.

Por lo tanto, es posible obtener una vista inconsistente del valor de un elemento en el diccionario.

Oded
fuente
2
¡Ese es un punto interesante! ¿Seguiría usando un candado en ese escenario?
Jon
@Jon: depende de tu aplicación y de si está bien, ¿quieres? Pero yo diría que si desea obtener vistas coherentes de los elementos, deberá cerrar cada lectura y actualización de un elemento en un candado.
Oded el
12
Creo que esto no es lo que dice el doctor. La inconsistencia tiene que ver con lo que contiene la vista, si la vista es solo el valor, entonces es perfectamente consistente. Siempre que obtenga el valor de una clave, el valor de la clave en el diccionario podría cambiar. Esto es tan inconsistente como el valor DateTime.Now.
George Mavritsakis
4

La mejor forma de averiguarlo es consultar la documentación de MSDN.

Para ConcurrentDictionary, la página es http://msdn.microsoft.com/en-us/library/dd287191.aspx

En la sección de seguridad de subprocesos, se indica "Todos los miembros públicos y protegidos de ConcurrentDictionary (Of TKey, TValue) son seguros para subprocesos y pueden usarse simultáneamente desde varios subprocesos".

Entonces, desde el punto de vista de la concurrencia, estás bien.

Erdem
fuente
2

Sí, tiene usted razón.

Eso y la posibilidad de enumerar el diccionario en un hilo mientras lo cambia en otro hilo son los únicos medios de existencia para esa clase.

ene
fuente
9
Lo que quisiera agregar es que aquí hay información útil sobre cómo y cuándo usar ConcurrentDictionary.
alex.b
1

Depende, en mi caso prefiero usar este método.

ConcurrentDictionary<TKey, TValue>.AddOrUpdate Method (TKey, Func<TKey, TValue>, Func<TKey, TValue, TValue>);

Consulte MSDN Library para obtener detalles sobre el uso del método.

Uso de muestra:

results.AddOrUpdate(
  Id,
  id => new DbResult() {
     Id = id,
     Value = row.Value,
     Rank = 1
  },
  (id, v) =>
  {
     v.Rank++;
     return v;
  });
Onur
fuente
2
FYI: "Cuando proporcionas un método de fábrica de valor (a los métodos GetOrAdd y AddOrUpdate), se puede ejecutar y descartar su resultado después (porque otro hilo había ganado la carrera)". Más información aquí: arbel.net/2013/02/03/…
keremispirli
Sí, tiene razón, como se indica en la sección de comentarios "Si llama a AddOrUpdate simultáneamente en diferentes subprocesos, addValueFactory se puede llamar varias veces, pero su par clave / valor podría no agregarse al diccionario para cada llamada". Por lo tanto, debe asegurarse de no generar varios objetos persistentes.
Onur
Y si necesita actualizar el contenido, sin cambiar el objeto almacenado por completo, por ejemplo, para cambiar una propiedad de un objeto agregado anteriormente, este método es útil; de lo contrario, debe usar bloqueos u otros métodos de sincronización.
Onur
1

Solo una nota: no justifica el uso de un objeto ConcurrentDicitonary con un bucle lineal, lo que lo hace infrautilizado. La mejor alternativa es seguir las recomendaciones de la Documentación de Microsoft, como menciona Oded usando Parallelism, de acuerdo con el siguiente ejemplo:

Parallel.For(0, 4, i => 
{
   myDic.TryAdd(i, 0);
});
Antonio Leonardo
fuente