2010-05-25 18 views
8

Estoy intentando actualizar las entradas en un ConcurrentDictionary algo como esto:campos de actualización de los valores de una ConcurrentDictionary

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 
     } 
    } 
} 

Básicamente necesito para repetir 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 this blog entry en el blog del equipo PFX: Quizás necesito 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 para hilos.

+0

es que hay realmente ninguna concurrencia pasando Si sólo alguna vez actualiza un campo de cada valor cada 30 segundos? Suena como un diccionario normal y un ciclo foreach debería ser todo lo que necesita. Parallel.ForEach debería funcionar también, ya que no hay recursos compartidos. – dtb

+0

Mientras se ejecuta el subproceso de actualización, se agregarán y eliminarán objetos adicionales del diccionario de otros subprocesos. Además, otras propiedades en el objeto se actualizarán a través de otros hilos en respuesta a los mensajes de WCF. Entonces sí, hay mucha concurrencia pasando. ¡Gracias por tu ayuda! – rboarman

+0

De acuerdo. Entonces mi respuesta persiste. Use AddOrUpdate/GetOrAdd/TryUpdate, etc. para manipular los contenidos del diccionario; tomar una instantánea del diccionario al iterarlo; bloquear las instancias individuales de Class1 al cambiar sus propiedades. – dtb

Respuesta

12

En primer lugar, 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. 
    } 
} 

iteración los justos valores deberían ser más rápido que la iteración del par de valores clave. [Aunque creo que se repite una lista de claves y haciendo los look-ups será más rápido todavía en el 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()); 
    } 

} 
3

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

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

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

Sin embargo, si quiere actualizar el contador para una clave específica, ConcurrentDictionary que puede ayudar con la adición atómicamente un nuevo par de valores clave si no existe la clave:

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

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

+0

Otra solución de la gente de MS se publicó aquí: http://social.msdn.microsoft.com/Forums/en-US/parallelextensions/thread/64f0c314-e3eb-457a-9dad-14d13cf06d68 Voy a probar ambos sus sugerencias y las de ellos e informar de nuevo. Gracias de nuevo. – rboarman

Cuestiones relacionadas