c# concurrency .net-4.0

c# - Actualización de campos de valores en un ConcurrentDictionary



concurrency .net-4.0 (2)

Estoy tratando de actualizar las entradas en un ConcurrentDictionary algo así:

class Class1 { public int Counter { get; set; } } class Test { private ConcurrentDictionary<int, Class1> dict = new ConcurrentDictionary<int, Class1>(); public void TestIt() { foreach (var foo in dict) { foo.Value.Counter = foo.Value.Counter + 1; // Simplified example } } }

Esencialmente necesito iterar sobre el diccionario y actualizar un campo en cada valor. Entiendo por la documentación que necesito evitar el uso de la propiedad Value. En cambio, creo que necesito usar TryUpdate, excepto que no quiero reemplazar todo mi objeto. En cambio, quiero actualizar un campo en el objeto.

Después de leer esta entrada de blog en el blog del equipo de PFX: Tal vez necesite usar AddOrUpdate y simplemente no hacer nada en el delegado de agregar.

¿Alguien tiene alguna idea de cómo hacer esto?

Tengo decenas de miles de objetos en el diccionario que necesito actualizar cada treinta segundos más o menos. Crear nuevos para actualizar la propiedad probablemente no sea factible. Tendría que clonar el objeto existente, actualizarlo y reemplazar el que está en el diccionario. También necesitaría bloquearlo durante la duración del ciclo de clonar / agregar. Yuck.

Lo que me gustaría hacer es iterar sobre los objetos y actualizar la propiedad del Contador directamente si es posible.

Mi última investigación me ha llevado a Parallel.ForEach, que suena muy bien, pero no se supone que deba usarse para acciones que actualizan el estado.

También vi mención de Interlocked.Increment que suena genial, pero aún necesito descubrir cómo usarlo en cada elemento de mi diccionario de forma segura.


ConcurrentDictionary no lo ayuda a acceder a los miembros de los valores almacenados al mismo tiempo, solo con los elementos mismos.

Si hay varios hilos que llaman a TestIt, debe obtener una instantánea de la colección y bloquear los recursos compartidos (que son los valores individuales del diccionario):

foreach (KeyValuePair<int, Class1> kvp in dict.ToArray()) { Class1 value = kvp.Value; lock (value) { value.Counter = value.Counter + 1; } }

Sin embargo, si desea actualizar el contador para una clave específica, ConcurrentDictionary puede ayudarlo a agregar atómicamente un nuevo par de valor clave si la clave no existe:

Class1 value = dict.GetOrAdd(42, key => new Class1()); lock (value) { value.Counter = value.Counter + 1; }

AddOrUpdate y TryUpdate de hecho son para casos en los que desea reemplazar el valor de una clave determinada en ConcurrentDictionary. Pero, como dijiste, no deseas cambiar el valor, quieres cambiar una propiedad del valor.


Primero, para resolver su problema de bloqueo:

class Class1 { // this must be a variable so that we can pass it by ref into Interlocked.Increment. private int counter; public int Counter { get{return counter; } } public void Increment() { // this is about as thread safe as you can get. // From MSDN: Increments a specified variable and stores the result, as an atomic operation. Interlocked.Increment(ref counter); // you can return the result of Increment if you want the new value, //but DO NOT set the counter to the result :[i.e. counter = Interlocked.Increment(ref counter);] This will break the atomicity. } }

Iterar los valores justos debería ser más rápido que repetir el par de valores clave. [Aunque creo que iterar una lista de claves y hacer las búsquedas será aún más rápido en ConcurrentDictionary en la mayoría de las situaciones.]

class Test { private ConcurrentDictionary<int, Class1> dictionary = new ConcurrentDictionary<int, Class1>(); public void TestIt() { foreach (var foo in dictionary.Values) { foo.Increment(); } } public void TestItParallel() { Parallel.ForEach(dictionary.Values,x=>x.Increment() ); } }