No pude encontrar suficiente información sobre los ConcurrentDictionary
tipos, así que pensé en preguntar aquí.
Actualmente, utilizo a Dictionary
para mantener a todos los usuarios a los que acceden constantemente múltiples subprocesos (desde un grupo de subprocesos, por lo que no hay una cantidad exacta de subprocesos), y tiene acceso sincronizado.
Recientemente descubrí que había un conjunto de colecciones seguras para subprocesos en .NET 4.0, y parece ser muy agradable. Me preguntaba, cuál sería la opción 'más eficiente y más fácil de administrar', ya que tengo la opción entre tener un Dictionary
acceso normal con acceso sincronizado o tener uno ConcurrentDictionary
que ya sea seguro para subprocesos.
Respuestas:
Una colección segura para subprocesos frente a una colección no segura para subprocesos puede considerarse de una manera diferente.
Considere una tienda sin empleado, excepto al momento de pagar. Tienes muchos problemas si las personas no actúan de manera responsable. Por ejemplo, digamos que un cliente toma una lata de una lata piramidal mientras un empleado está construyendo la pirámide, todo el infierno se desataría. O, ¿qué pasa si dos clientes alcanzan el mismo artículo al mismo tiempo, quién gana? ¿Habrá una pelea? Esta es una colección no segura para subprocesos. Hay muchas maneras de evitar problemas, pero todas requieren algún tipo de bloqueo, o más bien acceso explícito de una forma u otra.
Por otro lado, considere una tienda con un empleado en un escritorio, y solo puede comprar a través de él. Te pones en la fila y le pides un artículo, él te lo devuelve y sales de la fila. Si necesita varios artículos, solo puede recoger tantos artículos en cada viaje de ida y vuelta como pueda recordar, pero debe tener cuidado para evitar acaparar al empleado, esto enojará a los otros clientes en la fila detrás de usted.
Ahora considera esto. En la tienda con un empleado, ¿qué pasa si llegas hasta el frente de la fila y le preguntas al empleado "¿Tienes papel higiénico?", Y él dice "Sí", y luego dices "Ok, yo" me pondré en contacto contigo cuando sepa cuánto necesito ", y para cuando vuelvas a estar al frente de la fila, la tienda, por supuesto, se puede agotar. Este escenario no se evita mediante una colección segura para subprocesos.
Una colección segura para subprocesos garantiza que sus estructuras de datos internas sean válidas en todo momento, incluso si se accede desde múltiples subprocesos.
Una colección que no sea segura para subprocesos no viene con tales garantías. Por ejemplo, si agrega algo a un árbol binario en un subproceso, mientras que otro subproceso está ocupado reequilibrando el árbol, no hay garantía de que el elemento se agregará, o incluso si el árbol sigue siendo válido después, podría estar corrupto más allá de la esperanza.
Sin embargo, una colección segura para subprocesos no garantiza que todas las operaciones secuenciales en el subproceso funcionen en la misma "instantánea" de su estructura de datos interna, lo que significa que si tiene un código como este:
es posible que obtenga una NullReferenceException porque entre
tree.Count
ytree.First()
, otro subproceso ha eliminado los nodos restantes en el árbol, lo que significaFirst()
que volveránull
.Para este escenario, debe ver si la colección en cuestión tiene una forma segura de obtener lo que desea, tal vez necesite reescribir el código anterior o deba bloquear.
fuente
Dictionary
y manejar el bloqueo usted mismo versus usar elConcurrentDictionary
tipo que está integrado en .NET 4+. En realidad estoy un poco desconcertado de que esto haya sido aceptado.Aún debe ser muy cuidadoso al usar colecciones seguras para subprocesos porque no puede ignorar todos los problemas de subprocesos. Cuando una colección se anuncia a sí misma como segura para subprocesos, generalmente significa que permanece en un estado consistente incluso cuando varios subprocesos están leyendo y escribiendo simultáneamente. Pero eso no significa que un solo hilo verá una secuencia "lógica" de resultados si llama a varios métodos.
Por ejemplo, si primero verifica si existe una clave y luego obtiene el valor que corresponde a la clave, es posible que esa clave ya no exista incluso con una versión ConcurrentDictionary (porque otro hilo podría haber eliminado la clave). Aún necesita usar el bloqueo en este caso (o mejor: combine las dos llamadas usando TryGetValue ).
Así que úselos, pero no piense que le da un pase libre para ignorar todos los problemas de concurrencia. Aún debes tener cuidado.
fuente
TryGetValue
siempre ha sido parteDictionary
y siempre ha sido el enfoque recomendado. Los métodos "concurrentes" importantes queConcurrentDictionary
presenta sonAddOrUpdate
yGetOrAdd
. Entonces, buena respuesta, pero podría haber escogido un mejor ejemplo.Internally ConcurrentDictionary utiliza un bloqueo separado para cada cubo de hash. Siempre que use solo Add / TryGetValue y métodos similares que funcionan en entradas individuales, el diccionario funcionará como una estructura de datos casi libre de bloqueos con el respectivo beneficio de rendimiento dulce. OTOH, los métodos de enumeración (incluida la propiedad Count) bloquean todos los depósitos a la vez y, por lo tanto, son peores que un diccionario sincronizado, en cuanto al rendimiento.
Yo diría que solo use ConcurrentDictionary.
fuente
Creo que el método ConcurrentDictionary.GetOrAdd es exactamente lo que necesitan la mayoría de los escenarios de subprocesos múltiples.
fuente
¿Has visto las extensiones reactivas para .Net 3.5sp1? Según Jon Skeet, han respaldado un paquete de extensiones paralelas y estructuras de datos concurrentes para .Net3.5 sp1.
Hay un conjunto de muestras para .Net 4 Beta 2, que describe con bastante buen detalle cómo usarlas las extensiones paralelas.
Acabo de pasar la última semana probando el ConcurrentDictionary usando 32 hilos para realizar E / S. Parece funcionar como se anuncia, lo que indicaría que se ha realizado una gran cantidad de pruebas.
Editar : .NET 4 ConcurrentDictionary y patrones.
Microsoft ha lanzado un pdf llamado Patterns of Paralell Programming. Realmente vale la pena descargarlo, ya que describe con detalles realmente agradables los patrones correctos para usar con las extensiones concurrentes de .Net 4 y los patrones anti para evitar. Aquí está.
fuente
Básicamente quieres ir con el nuevo ConcurrentDictionary. Desde el primer momento, debe escribir menos código para que los programas sean seguros para subprocesos.
fuente
El
ConcurrentDictionary
es una gran opción si cumple todas sus necesidades de hilo de seguridad. Si no es así, en otras palabras, está haciendo algo un poco complejo, unDictionary
+ normallock
puede ser una mejor opción. Por ejemplo, supongamos que está agregando algunas órdenes a un diccionario y desea mantener actualizado el monto total de las órdenes. Puede escribir código como este:Este código no es seguro para subprocesos. Varios subprocesos que actualizan el
_totalAmount
campo pueden dejarlo en un estado dañado. Por lo tanto, puede intentar protegerlo conlock
:Este código es "más seguro", pero aún no es seguro para subprocesos. No hay garantía de que
_totalAmount
sea consistente con las entradas en el diccionario. Otro hilo puede intentar leer estos valores, para actualizar un elemento de la interfaz de usuario:Es
totalAmount
posible que no correspondan al recuento de pedidos en el diccionario. Las estadísticas mostradas pueden estar equivocadas. En este punto, se dará cuenta de que debe extender lalock
protección para incluir la actualización del diccionario:Este código es perfectamente seguro, pero todos los beneficios de usar a
ConcurrentDictionary
se han ido.Dictionary
, ya que el bloqueo interno dentro delConcurrentDictionary
dispositivo ahora es derrochador y redundante.TryAdd
?,AddOrUpdate
?).Entonces, mi consejo es: comience con un
Dictionary
+lock
y mantenga la opción de actualizar más tarde a aConcurrentDictionary
como una optimización del rendimiento, si esta opción es realmente viable. En muchos casos no lo será.fuente
Hemos utilizado ConcurrentDictionary para la recopilación en caché, que se vuelve a llenar cada 1 hora y luego se lee por varios subprocesos de clientes, similar a la solución para ¿Es seguro este subproceso de ejemplo? pregunta.
Descubrimos que cambiarlo a ReadOnlyDictionary mejoraba el rendimiento general.
fuente