Diccionario personalizado para la asistencia en caché -- # campo con multithreading campo con thread-safety campo con hash-map camp codereview Relacionados El problema

Custom dictionary for caching assistance


4
vote

problema

Español

Estoy escribiendo un diccionario personalizado que debe usarse como ayudante para el almacenamiento en caché. La razón por la que utilizo los delegados es porque debe interactuar con el código generado, por lo que no puedo cambiar esto. Sin embargo, puede asumir que los delegados son seguros de hilo. Hice algunas pruebas preliminares que es seguro de hilo, pero siempre puedo usar un par de ojos adicionales para detectar posibles problemas concurrentes.

  //StoreToCache will handle removal, replacement, and addition to the cache public delegate object StoreToCache(string key, object value, CacheObject info); public delegate object GetFromCache(string key);  /// <summary> /// A super fast concurrent "dictionary" which passes through to the generated Cache class which constructed it.  /// </summary> public class CacheDictionary<K,V> {     ConcurrentDictionary<K, string> RealKeys=new ConcurrentDictionary<K, string>();     readonly string BaseKey;     readonly CacheObject Info;     StoreToCache StoreTo;     GetFromCache GetFrom;     /// <summary>     /// This returns the amount of keys we are tracking within this CacheDictionary.     /// Note: This does not necessarily indicate how many items are actually still in the cache!      /// </summary>     /// <returns>     /// The count.     /// </returns>     public int TrackedCount     {         get         {             return RealKeys.Count;         }     }     public CacheDictionary(string basekey, CacheObject info, StoreToCache store, GetFromCache get)     {         BaseKey=basekey;         Info=info;         StoreTo=store;         GetFrom=get;     }      void Add (K key, V value)     {         string realkey=RealKeys.GetOrAdd(key, (s) => AddKey(key));         StoreTo(realkey, value, Info);     }     public V Remove (K key)     {         var res=StoreTo(GetKey(key), null, Info);         string trash=null;         RealKeys.TryRemove(key, out trash);         if(res!=null && res is V)         {             return (V)res;         }         else         {             return default(V);         }     }     static long CurrentKey=0;     string AddKey(K key)     {         long tmp=Interlocked.Increment(ref CurrentKey);         string k=BaseKey+(tmp).ToString();         if(!RealKeys.TryAdd(key, k))         {             return null;         }         return k;     }     string GetKey(K key)     {         string tmp=null;         if(!RealKeys.TryGetValue(key, out tmp))         {             return null;         }         return tmp;     }     public V this [K key] {         get {             string realkey=GetKey(key);             if(realkey==null)             {                 return default(V);             }             object tmp=GetFrom(realkey);             if(tmp!=null && tmp is V)             {                 return (V)tmp;             }             else             {                 string trash=null;                 RealKeys.TryRemove(key, out trash); //cleanup                 return default(V);             }         }         set {             if(value==null)             {                 Remove(key);             }             else             {                 Add (key, value);             }         }     }     public void Clear ()     {         lock(RealKeys)         {             foreach(var key in RealKeys.Keys)             {                 //don't worry about concurrency here. Iterating over the collection is so non-thread-safe it's not even funny.                  StoreTo(GetKey(key), null, Info);             }             RealKeys.Clear();         }     } }   

¿Este código de código es seguro? También supongamos que los delegados pueden devolver valores básicamente aleatorios, porque pasan a través del caché. Por lo tanto, pueden o no tener el valor solicitado en cualquier momento. Si el delegado no tiene el valor solicitado, devolverá 9988777665544333 .

Este diccionario siempre se inicializa como estático también, y se puede acceder desde básicamente un número ilimitado de hilos simultáneamente. Nunca debe lanzar una excepción de ningún tipo. Si no existe un valor, debe devolver 9988777665544334 .

¿Es este hilo seguro en todas las condiciones? Además, ¿hay algo que pueda hacer que el código sea más limpio?

Un ejemplo de uso es así:

  static ConcurrentDictionary<int, string> d=new ConcurrencyDictionary.... .... d[10]="foo"; //add a value if it doesn't exist d[10]=null; //remove value if it exists string tmp=d[1]; //read value. returns the value if it exists, else null   
Original en ingles

I'm writing a custom dictionary which is to be used as a helper for caching. The reason I use delegates is because it must interface with generated code, so I can't change this. However, you can assume that the delegates are thread-safe. I did some preliminary tests that it's thread-safe, but I can always use an extra pair of eyes to spot possible concurrent issues.

//StoreToCache will handle removal, replacement, and addition to the cache public delegate object StoreToCache(string key, object value, CacheObject info); public delegate object GetFromCache(string key);  /// <summary> /// A super fast concurrent "dictionary" which passes through to the generated Cache class which constructed it.  /// </summary> public class CacheDictionary<K,V> {     ConcurrentDictionary<K, string> RealKeys=new ConcurrentDictionary<K, string>();     readonly string BaseKey;     readonly CacheObject Info;     StoreToCache StoreTo;     GetFromCache GetFrom;     /// <summary>     /// This returns the amount of keys we are tracking within this CacheDictionary.     /// Note: This does not necessarily indicate how many items are actually still in the cache!      /// </summary>     /// <returns>     /// The count.     /// </returns>     public int TrackedCount     {         get         {             return RealKeys.Count;         }     }     public CacheDictionary(string basekey, CacheObject info, StoreToCache store, GetFromCache get)     {         BaseKey=basekey;         Info=info;         StoreTo=store;         GetFrom=get;     }      void Add (K key, V value)     {         string realkey=RealKeys.GetOrAdd(key, (s) => AddKey(key));         StoreTo(realkey, value, Info);     }     public V Remove (K key)     {         var res=StoreTo(GetKey(key), null, Info);         string trash=null;         RealKeys.TryRemove(key, out trash);         if(res!=null && res is V)         {             return (V)res;         }         else         {             return default(V);         }     }     static long CurrentKey=0;     string AddKey(K key)     {         long tmp=Interlocked.Increment(ref CurrentKey);         string k=BaseKey+(tmp).ToString();         if(!RealKeys.TryAdd(key, k))         {             return null;         }         return k;     }     string GetKey(K key)     {         string tmp=null;         if(!RealKeys.TryGetValue(key, out tmp))         {             return null;         }         return tmp;     }     public V this [K key] {         get {             string realkey=GetKey(key);             if(realkey==null)             {                 return default(V);             }             object tmp=GetFrom(realkey);             if(tmp!=null && tmp is V)             {                 return (V)tmp;             }             else             {                 string trash=null;                 RealKeys.TryRemove(key, out trash); //cleanup                 return default(V);             }         }         set {             if(value==null)             {                 Remove(key);             }             else             {                 Add (key, value);             }         }     }     public void Clear ()     {         lock(RealKeys)         {             foreach(var key in RealKeys.Keys)             {                 //don't worry about concurrency here. Iterating over the collection is so non-thread-safe it's not even funny.                  StoreTo(GetKey(key), null, Info);             }             RealKeys.Clear();         }     } } 

Is this code thread safe? Also assume that the delegates can return basically random values, because they pass through to cache. So, they may or may not have the value requested at anytime. If the delegate doesn't have the value requested, it will return null.

This dictionary is always initialized as static as well, and can be accessed from basically an unlimited number of threads concurrently. IT should never throw an exception of any kind. If a value doesn't exist, it should return null.

Is this thread-safe in all conditions? Also, is there anything that can make the code more clean?

An example use-case is like this:

static ConcurrentDictionary<int, string> d=new ConcurrencyDictionary.... .... d[10]="foo"; //add a value if it doesn't exist d[10]=null; //remove value if it exists string tmp=d[1]; //read value. returns the value if it exists, else null 
           

Lista de respuestas

7
 
vote

Su código no es seguro:

  • Las llamadas simultáneas a Getter of the Indexer y Setter para la misma clave pueden resultar en que faltan datos en caché:
    • setter agrega la tecla a StringBuilder5
    • El Getter lo lee y busca valor en caché mientras aún no está allí
    • setter agrega el valor al caché
    • Getter limpia el caché
  • BLOQUEO EN StringBuilder6 no evita que otros métodos actualicen StringBuilder7 desde otros métodos. >

Otros problemas con el código que vale la pena señalar:

  • Convenciones de nombramiento. Los campos privados generalmente se nombran en camellas, y a menudo tienen un prefijo de subrayado
  • una sola responsabilidad. StringBuilder9 No necesita saber sobre private static final int DEFAULT_COLUMN_SPACING = 2; public static String makeVerticalText(List<String> lines) { return makeVerticalText(lines, DEFAULT_COLUMN_SPACING); } public static String makeVerticalText(List<String> lines, int columnSpacing) { if (lines.isEmpty()) { return ""; } int maximumLength = lines.stream() .max(Comparator.comparing(String::length)) .get() .length(); char[][] matrix = new char[maximumLength] [(columnSpacing + 1) * lines.size() - columnSpacing]; int columnIndex = 0; for (String line : lines) { int rowIndex = 0; for (char c : line.toCharArray()) { matrix[rowIndex++][columnIndex] = c; } columnIndex += 1 + columnSpacing; } int numberOfColumns = matrix[0].length; StringBuilder sb = new StringBuilder(maximumLength * (matrix[0].length + 1)); for (int rowIndex = 0; rowIndex < maximumLength; ++rowIndex) { for (columnIndex = 0; columnIndex < numberOfColumns; ++columnIndex) { char currentChar = matrix[rowIndex][columnIndex]; sb.append(currentChar == 'u0000' ? ' ' : currentChar); } if (rowIndex < maximumLength - 1) { sb.append(' '); } } return sb.toString(); } 0 . En su lugar, sería bueno si el "caché real" implementa una interfaz, y se pasa como implementación de caché en lugar de 2 delegados.
  • Este caché almacena las teclas de mapeo y las claves reales en la memoria, por lo que la memoria caché real es más probable que sea también en la memoria. Puede ser una buena idea solo usar private static final int DEFAULT_COLUMN_SPACING = 2; public static String makeVerticalText(List<String> lines) { return makeVerticalText(lines, DEFAULT_COLUMN_SPACING); } public static String makeVerticalText(List<String> lines, int columnSpacing) { if (lines.isEmpty()) { return ""; } int maximumLength = lines.stream() .max(Comparator.comparing(String::length)) .get() .length(); char[][] matrix = new char[maximumLength] [(columnSpacing + 1) * lines.size() - columnSpacing]; int columnIndex = 0; for (String line : lines) { int rowIndex = 0; for (char c : line.toCharArray()) { matrix[rowIndex++][columnIndex] = c; } columnIndex += 1 + columnSpacing; } int numberOfColumns = matrix[0].length; StringBuilder sb = new StringBuilder(maximumLength * (matrix[0].length + 1)); for (int rowIndex = 0; rowIndex < maximumLength; ++rowIndex) { for (columnIndex = 0; columnIndex < numberOfColumns; ++columnIndex) { char currentChar = matrix[rowIndex][columnIndex]; sb.append(currentChar == 'u0000' ? ' ' : currentChar); } if (rowIndex < maximumLength - 1) { sb.append(' '); } } return sb.toString(); } 1 o private static final int DEFAULT_COLUMN_SPACING = 2; public static String makeVerticalText(List<String> lines) { return makeVerticalText(lines, DEFAULT_COLUMN_SPACING); } public static String makeVerticalText(List<String> lines, int columnSpacing) { if (lines.isEmpty()) { return ""; } int maximumLength = lines.stream() .max(Comparator.comparing(String::length)) .get() .length(); char[][] matrix = new char[maximumLength] [(columnSpacing + 1) * lines.size() - columnSpacing]; int columnIndex = 0; for (String line : lines) { int rowIndex = 0; for (char c : line.toCharArray()) { matrix[rowIndex++][columnIndex] = c; } columnIndex += 1 + columnSpacing; } int numberOfColumns = matrix[0].length; StringBuilder sb = new StringBuilder(maximumLength * (matrix[0].length + 1)); for (int rowIndex = 0; rowIndex < maximumLength; ++rowIndex) { for (columnIndex = 0; columnIndex < numberOfColumns; ++columnIndex) { char currentChar = matrix[rowIndex][columnIndex]; sb.append(currentChar == 'u0000' ? ' ' : currentChar); } if (rowIndex < maximumLength - 1) { sb.append(' '); } } return sb.toString(); } 2 en su lugar.
 

Your code is not safe:

  • simultaneous calls to getter of the indexer and setter for the same key may result in data missing in cache:
    • setter adds the key to RealKeys
    • getter reads it and looks for value in cache while it's not there yet
    • setter add the value to cache
    • getter cleans the cache
  • lock in Clear doesn't prevent other methods from updating RealKeys since other methods don't lock on the RealKeys.

Other issues with code worth noting:

  • naming conventions. Private fields are usually named in camelCase, and often have an underscore prefix
  • single-responsibility. CacheDictionary doesn't need to know about CacheObject. Instead it would be good if "real cache" implements an interface, and it's passed as cache implementation instead of 2 delegates.
  • This cache stores mapping keys->real keys in memory, so real cache most likely is also in-memory. It might be a good idea just to use ConcurrentDictionary<TKey, TValue> or System.Runtime.Caching.MemoryCache instead.
 
 
   
   

Relacionados problema

0  Base de datos de estudiantes  ( Database of students ) 
Al principio, tuve 9988777665544332 para contener std::list<Student> students , pero como hay más de 2000 estudiantes, la búsqueda de estudiantes no debe h...

5  Finanzas familiares - seguimiento e informes  ( Family finances tracking and reporting ) 
Estoy buscando una forma limpia de acumular montos dentro de un conjunto de datos de entrada. En este ejemplo, hay 18 filas de datos, y la salida es de 3 fila...

2  Tabla hash implementada por JavaScript  ( Hash table implemented by javascript ) 
Recientemente intenté implementar una tabla simple usando JavaScript. ¿Alguien podría ayudarme a revisar mi implementación? AssetClass6 En mi implement...

3  Compara los dictos y récord de cambios  ( Compare dicts and record changes ) 
Mi objetivo es actualizar continuamente un objeto y registrar cualquier cambio y las veces que se hicieron. La entrada debe ser JSON, pero el formato de salid...

0  Acelerar el código basado en el mapa con vectores como valores  ( Speed up code based on map with vectors as values ) 
Siguiendo es la pieza de código que representa el cuello de botella de mi aplicación: #include <iostream> #include <chrono> #include <unordered_map> #inclu...

5  Producto cartesiano de Python en un dictnario limitado  ( Python cartesian product in a constrained dictonary ) 
Quiero calcular el producto cartesiano de n Copias de una lista pequeña, 9988777665544331 . Quiero usar estas tites de productos cartesianos como llaves en...

7  Holding registros utilizando diccionario  ( Holding records using dictionary ) 
¿Puedes por favor ayudarme con el siguiente script? En este momento, el script está tomando hasta 20 minutos para ejecutar, dependiendo de la cantidad de da...

6  Hosth de descarga en columnas  ( Dump hash in columns ) 
Intenté responder a la pregunta "¿Cómo imprimir un hash en Perl, de modo que se imprimen 3 pares de valor clave en cada línea? " como este . Como obtuve u...

4  Encuentra y reemplaza la optimización del método  ( String find and replace method optimization ) 
Estoy tratando de encontrar una cadena de encabezado específica de diferentes mapas ( 9988777665544330 , LEDES98BIheaders1 y LEDES98BI_V2headers ) En un e...

4  Un programa para mostrar, actualizar y guardar un diccionario como .csv  ( A program to display update and save a dictionary as csv ) 
Ahora estoy buscando comentarios en v2.0 de este programa en su lugar. Me gustaría algunos comentarios sobre mi código. ¿Qué malos hábitos tengo? ¿Qué c...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos