2009-12-22 13 views
99

bloqueo no pude encontrar suficiente información sobre ConcurrentDictionary tipos, así que pensé en preguntar sobre ello aquí..NET - Diccionario vs ConcurrentDictionary

Actualmente, utilizo un Dictionary para contener a todos los usuarios a los que se accede constantemente por varios subprocesos (desde un grupo de subprocesos, por lo que no hay cantidad exacta de subprocesos), y tiene acceso sincronizado.

Recientemente descubrí que había un conjunto de colecciones seguras para hilos en .NET 4.0, y parece ser muy agradable. Me preguntaba, ¿cuál sería la opción 'más eficiente y fácil de administrar', ya que tengo la opción entre tener un Dictionary normal con acceso sincronizado o tener un ConcurrentDictionary que ya es seguro para subprocesos.

Reference to .NET 4.0's ConcurrentDictionary

+2

Es posible que desee ver [este artículo del proyecto de código sobre el tema] (http: //www.codeproject.com/Articles/548406/Dictionary-plus-Locking-versus-ConcurrentDictionar) – nawfal

Respuesta

124

Una colección multi-hilo frente a un no-hilo-recolección puede considerarse de una manera diferente.

Considérese una tienda con ningún empleado, excepto al momento de pagar. Tienes un montón de problemas si las personas no actúan responsablemente. Por ejemplo, digamos que un cliente toma una lata de una lata de pirámide mientras un empleado está construyendo la pirámide, todo el infierno se desataría. O bien, ¿qué pasa si dos clientes alcanzan el mismo artículo al mismo tiempo, quién gana? ¿Habrá una pelea? Esta es una colección sin hilos. Hay muchas maneras de evitar problemas, pero todos requieren algún tipo de bloqueo, o acceso más bien 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. Usted se pone en línea y le pide un artículo, se lo devuelve y se sale de la fila. Si necesita varios artículos, solo puede recoger tantos artículos en cada viaje de ida y vuelta como lo recuerde, pero debe tener cuidado para evitar acaparar al empleado, esto enojará a los otros clientes detrás de usted.

Ahora considere esto. En la tienda con un empleado, ¿qué sucede si llega hasta el frente de la fila y pregunta al empleado "¿Tiene papel higiénico?", Y él responde "Sí", y luego dice "Ok, yo". Me pondré en contacto contigo cuando sepa cuánto necesito ", entonces, cuando vuelvas a estar al frente de la línea, la tienda puede agotarse. Esta situación no se previene con una colección segura.

Una colección threadsafe garantiza que sus estructuras de datos internos son válidos en todo momento, incluso si accede desde varios subprocesos.

Una colección que no es para roscar no viene con tales garantías. Por ejemplo, si agrega algo a un árbol binario en un hilo, mientras otro hilo está ocupado reequilibrando el árbol, no hay garantía de que el elemento se agregará, o incluso que el árbol sigue siendo válido después, podría estar dañado más allá de toda esperanza.

Una colección multi-hilo no obstante, garantiza que las operaciones secuenciales en el hilo de todo el trabajo en la misma "instantánea" de su estructura interna de datos, lo que significa que si usted tiene un código como éste:

if (tree.Count > 0) 
    Debug.WriteLine(tree.First().ToString()); 

puede obtener una NullReferenceException porque entre medio y tree.Counttree.First(), otro hilo ha limpiado el resto de nodos en el árbol, lo que significa First() volverá null.

Para este escenario, o necesita ver si la colección en cuestión tiene una forma segura de obtener lo que desea, tal vez necesita volver a escribir el código anterior, o puede que necesite bloquear.

+7

Cada vez que leo este artículo, aunque es cierto, de alguna manera vamos por el camino equivocado. –

+18

El principal problema en una aplicación habilitada para hilos es la estructura de datos mutable. Si puedes evitar eso, te ahorrarás montones de problemas. –

+0

Al parecer, los diccionarios pueden tener algunos efectos secundarios muy desagradables: http://stackoverflow.com/questions/14838032/asp-net-hang-generic-dictionary-concurrency-issues-causes-gc-deadlock – jocull

5

Básicamente, usted quiere ir con el nuevo ConcurrentDictionary. Nada más sacarlo de la caja, tiene que escribir menos código para hacer programas seguros para subprocesos.

+0

¿Hay algún problema si continúo con Concurrent Dictionery? – kbvishnu

53

Aún debe tener mucho cuidado al usar colecciones seguras para subprocesos porque la seguridad de subprocesos no significa que puede ignorar todos los problemas de subprocesamiento. Cuando una colección se anuncia a sí misma como segura para subprocesos, generalmente significa que permanece en un estado consistente incluso cuando varios hilos 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 múltiples métodos.

Por ejemplo, si primero comprueba si existe una clave y luego obtiene el valor que corresponde a la clave, esa clave puede que ya no exista con una versión ConcurrentDictionary (porque otra secuencia podría haber eliminado la clave). Aún necesita usar el bloqueo en este caso (o mejor: combine las dos llamadas usando TryGetValue).

también lo hacen uso de ellos, pero no creo que le da un pase libre para ignorar todos los problemas de concurrencia. Aún debes tener cuidado.

+4

¿Entonces básicamente está diciendo que si no usa el objeto correctamente, no funcionará? – ChaosPandion

+0

¿Por qué es eso? La colección pierde el cambio, de alguna manera? –

+1

Básicamente, debe usar TryAdd en lugar de los Contiene comunes -> Agregar. – ChaosPandion

12

Creo que el método ConcurrentDictionary.GetOrAdd es exactamente lo que la mayoría de los escenarios de múltiples subprocesos necesitan.

+1

Usaría TryGet también, de lo contrario perderá el esfuerzo inicializando el segundo parámetro en GetOrAdd cada vez que la clave ya exista. –

+5

* GetOrAdd * tiene la sobrecarga * GetOrAdd (TKey, Func ) *, por lo que el segundo parámetro puede ser de inicio diferido solo en caso de que la clave no exista en el diccionario. Además, * TryGetValue * se usa para buscar datos, no para modificarlos. Las llamadas posteriores a cada uno de estos métodos pueden provocar que otro subproceso pueda haber agregado o eliminado la clave entre las dos llamadas. – sgnsajgon

+0

Esta debería ser la respuesta correcta a la pregunta, aunque la publicación de Lasse es excelente. – Spivonious

13

¿Ha visto el Reactive Extensions 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 en detalle bastante bueno sobre cómo utilizarlos 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 han realizado una gran cantidad de pruebas.

Editar: .NET 4 ConcurrentDictionary y patrones.

Microsoft ha lanzado un pdf llamado Programación de Patrones de Paralell. Realmente vale la pena descargarlo, ya que describió con detalles realmente agradables los patrones correctos que se deben usar para las extensiones simultáneas de .NET 4 y los patrones anti a evitar. Here it is.

27

Internamente ConcurrentDictionary utiliza un bloqueo separado para cada cubo hash. Siempre que use solo Add/TryGetValue y métodos similares que funcionen en entradas individuales, el diccionario funcionará como una estructura de datos casi sin 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 términos de rendimiento.

Yo diría, simplemente use ConcurrentDictionary.

2

Hemos utilizado ConcurrentDictionary para la recolección en caché, que se rellena cada 1 hora y luego se lee por varios hilos del cliente, to the solution similar para la pregunta Is this example thread safe?.

Hemos encontrado, que la cambia por ReadOnlyDictionary mejoró el rendimiento general.

+2

Agradezco saber el motivo de la votación negativa? –

+0

ReadOnlyDictionary es solo el envoltorio de otro IDictionary. ¿Qué usas debajo del capó? – Lu55

Cuestiones relacionadas