2010-12-17 6 views
8

He de comenzar a trabajar con algo de código .NET 3.5 y encontró el siguiente método de extensión que se utilizan para una caché ::Para .NET 3.5, ¿cómo haría para obtener un caché o agregar caché?

public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> @this, TKey key,Func<TKey,TValue> factory,bool useLocking) 
{ 

    TValue value; 
    if([email protected](key,out value)) 
    { 
     if (useLocking) 
     { 
      lock ((@this as ICollection).SyncRoot) 
      { 
       if ([email protected](key, out value)) 
       { 
        @this[key] = value = factory(key); 
       } 
      } 
     } 
     else 
     { 
      @this[key] = value = factory(key); 
     } 
    } 
    return value; 
} 

El caché en cuestión está enchavetado mediante claves de cadena, y con useLocking = true. Siempre se accede a este método (no hay datos perdidos TryGetValue). Tampoco hay problema al utilizar la propiedad SyncRoot porque el diccionario es privado y en ningún otro lugar se está utilizando. El doble bloqueo es peligroso porque un diccionario no admite la lectura mientras se está escribiendo. Aunque técnicamente no se ha informado ningún problema aún, ya que el producto no se ha enviado, creo que este enfoque conducirá a las condiciones de carrera.

  1. Cambiar el Dictionary<,> a un Hashtable. Perderemos la seguridad de tipo, pero podremos soportar el modelo de concurrencia que buscamos (1 escritor, lectores múltiples).

  2. Elimina TryGetValue externo. De esa forma cada lectura, requiere adquirir un candado. Esto es potencialmente malo para el rendimiento, pero adquirir un bloqueo no probado debería ser bastante barato.

Ambos son bastante asquerosos. ¿Alguien tiene una mejor sugerencia? Si este fuera el código de .NET 4, simplemente lo cambiaría a ConcurrentDictionary, pero no tengo esa opción.

+5

Yargh! Completamente no relacionado ... pero no escribes código usando @this. Estás en un método estático; no intente hacer que se vea como un método de instancia. –

+0

¡este no es mi código! Si fuera mi código, agregaría validación de argumentos. –

Respuesta

0

Elimina TryGetValue. Apuesto a que no verá el problema de concurrencia; Los monitores CLR son bastante rápidos, y son "injustos" para que no veas convoy o problemas de inversión prioritarios.

Si ve un problema de simultaneidad, la siguiente mejor opción es un ReaderWriterLockSlim. Desafortunadamente, vas a querer crear una nueva clase para eso en lugar de usar un método de extensión, porque vas a necesitar un lugar para almacenar el bloqueo.

Si va por esa ruta, asegúrese de mantenerse alejado de la actualización del lector a las cerraduras del escritor.

0

Creo que debes bloquear la declaración completa (como dijiste).

Sin embargo, hay una buena solución en la web para prepararse para una futura actualización de .NET 4: tener una clase personalizada en lugar de un diccionario, mantener el diccionario como variable de miembro privado y ajustar todo el acceso al diccionario en un hilo -clave de bloqueo seguro Descripción detallada se puede encontrar aquí: http://www.grumpydev.com/2010/02/25/thread-safe-dictionarytkeytvalue/

2

Sí, sus sospechas son correctas; hay una condición de carrera y todos los métodos, incluso TryGetValue deben estar dentro del bloqueo en la implementación que usted presentó.

En cuanto al rendimiento, puede esperar el día en que pueda actualizar a .NET4, que incluye un rápido ConcurrentDictionary fuera de la caja. Hasta entonces, puede revisar el análisis de James Michael Hare hecho aquí:

Estos resultados sugieren que para mí la mejor aplicación para .NET3.5 es Dictionary más ReadWriteLockSlim y por si acaso, aquí es una implementación completa:

Actualización:

leí las tablas mal y parece que Dictionary + lock es poco más rápido que el único otro contendiente serio Dictionary + ReadWriteLockSlim.

+1

Es curioso, esos resultados sugieren a * me * que un 'Diccionario' con bloqueo mutex es la mejor implementación. Es más rápido en general que la implementación de rw-lock. – Gabe

+0

Vaya, analicé demasiado rápido y leí esa tabla como operaciones por unidad de tiempo, en lugar de tiempo por unidad de operaciones. –

+1

El artículo con la implementación completa Thread Safe Dictionary ha desaparecido, pero después de investigarlo creo que encontré la [implementación en sí misma] (http://www.codekeep.net/snippets/b2afc386-13cc-4297- a327-dbf52dd7498a.aspx) (solo código, sin comentario/explicación). – Dusty

1

Mi recomendación sería descargar Reactive Extensions for .NET (Rx), que incluye los respaldos de las colecciones en el espacio de nombres System.Collections.Concurrent (esto incluye ConcurrentDictionary<TKey, TValue>) para .NET 3.5.

+2

Esos ya no están allí desde el 19 de octubre de 2012. – rickythefox

Cuestiones relacionadas