thread safe example concurrentbag concurrent c# java multithreading collections

safe - synchronized dictionary c#



¿Tus colecciones son seguras para hilos? (17)

Al diseñar una clase de colección, ¿hay alguna razón para no implementar el bloqueo de forma privada para que sea seguro? ¿O debería dejar esa responsabilidad al consumidor de la colección?


¿Hay alguna razón para no implementar el bloqueo de forma privada para que sea seguro?

Depende. ¿Tu objetivo es escribir una clase de colección a la que accedan varios hilos? Si es así, hazlo seguro. Si no, no pierdas tu tiempo. Este tipo de cosas es a lo que la gente se refiere cuando hablan de ''optimización prematura''

Resuelve los problemas que tienes. No trates de resolver problemas futuros que creas que puedes tener en el futuro, porque no puedes ver el futuro e invariablemente estarás equivocado.

Nota: Aún necesita escribir su código de una manera mantenible, de modo que si tuviera que venir y agregar el bloqueo a la colección, no sería terriblemente difícil. Mi punto es "no implemente funciones que no necesita y no va a usar"


Básicamente, diseñe su colección como hilo seguro, con bloqueo implementado en dos métodos de su clase: lock () y unlock (). Llámalos a cualquier lugar que necesites, pero déjalos vacíos. Luego, subclase su colección implementando los métodos lock () y unlock (). Dos clases por el precio de uno.


Esto haría que sea imposible acceder simultáneamente a una colección de varios hilos, incluso si usted sabe que el elemento que toca no es utilizado por nadie más.

Un ejemplo sería una colección con un acceso a índice basado en un entero. Cada hilo puede saber desde su identificación a qué valores de índice puede acceder sin preocuparse por lecturas o escrituras sucias.

Otro caso en el que obtendría un golpe de rendimiento innecesario sería cuando los datos solo se leen de la colección y no se escriben.


Estoy de acuerdo en que dejarlo en manos del consumidor es el enfoque correcto. Si proporciona al consumidor mucha más flexibilidad en cuanto a si la instancia de Colección está sincronizada o si un objeto diferente está sincronizado. Por ejemplo, si tenía dos listas que necesitaban actualizarse ambas, podría tener sentido tenerlas en un solo bloque sincronizado utilizando un solo bloqueo.


Hacer que la colección sea segura para el hilo es lo que mató a las clases Vector y Hashtable de Java. Es mucho más fácil para un cliente envolverlo en un contenedor seguro para hilos, como se sugirió anteriormente, o sincronizar el acceso a datos en el subconjunto de métodos, que tomar un golpe de sincronización cada vez que se accede a la clase. Casi nadie usa Vector o Hashtable, y si lo hacen, se ríen de ellos, porque sus reemplazos (ArrayList y HashMap) son mucho más rápidos. Lo cual es desafortunado, ya que yo (proveniente de un fondo de C ++) prefiero mucho el nombre "Vector" (STL), pero ArrayList llegó para quedarse.


La razón principal para no hacerlo seguro es el rendimiento. El código de seguridad del hilo puede ser 100 veces más lento que el código no seguro, por lo que si el cliente no quiere la característica, es un desperdicio bastante grande.


Las clases de colección deben ser lo más rápidas posible. Por lo tanto, deje los bloqueos fuera.

El código de llamada sabrá dónde están mejor los bloqueos que la clase de colección no. En el peor de los casos, la aplicación tendrá que agregar un bloqueo adicional, lo que significa que se producen dos bloqueos, lo que duplica el golpe de rendimiento.


Para Java, debe dejar sin sincronizar para la velocidad. El consumidor de la colección puede envolver en un contenedor de sincronización si así lo desea.


Si estoy buscando una clase de colección y necesito capacidades seguras para subprocesos y su clase no las tiene, de inmediato me saltearé la siguiente oferta para ver qué ofrecen. Tu colección no captará más mi atención.

Tenga en cuenta el "Si" al comienzo. Algunos clientes lo querrán, otros no, y a otros no les importará. Si vas a construir un juego de herramientas para los consumidores, ¿por qué no ofrecer ambas variedades? De esa manera puedo elegir cuál utilizar, pero si quiero un hilo seguro, todavía tengo mi atención y no tengo que escribirlo yo mismo.


Si haces una clase de colección, no la hagas segura. Es bastante difícil hacerlo bien (por ejemplo, correcto y rápido), y los problemas para su consumidor cuando lo hace mal (heisenbugs) son difíciles de depurar.

En lugar de eso, implemente una de las API de recopilación y use Collections.synchronizedCollection (yourCollectionInstance) para obtener una implementación segura para subprocesos si la necesitan.

Simplemente consulte el método Collections.synchronizedXXX apropiado en su clase javadoc; dejará en claro que ha considerado la seguridad de subprocesos en su diseño y se aseguró de que el consumidor tenga a su disposición una opción segura para subprocesos.


Simplemente tenga en cuenta en su documentación que no lo está haciendo seguro y lo omite, o si, para su aplicación, desea que el hilo sea seguro, hágalo seguro y anótelo en su documentación correspondiente. La única regla es documentarlo. Aparte de eso, haga su clase por usted y si otras personas quieren usarlo, pueden hacerlo.


Tenga en cuenta que si intenta hacer que cualquier clase sea segura para subprocesos, debe decidir sobre los escenarios de uso común.

Por ejemplo, en el caso de una colección, el solo hecho de que todas las propiedades y métodos individualmente sean seguros para subprocesos podría no ser lo suficientemente bueno para un consumidor, ya que leer primero el recuento y luego iterar, o similar, no sería muy útil si cuenta cambiado después de leerlo.


Yo personalmente lo dejaría en manos de los consumidores. Hará que su clase de colección sea más genérica.


Una buena razón para NO hacer que su colección sea segura para subprocesos es para mejorar el rendimiento de un único subproceso. Ejemplo: ArrayList sobre Vector. Aplazar la seguridad de la hebra a la persona que llama permite que la caja de uso no sincronizada se optimice evitando el bloqueo.

Una buena razón para hacer que su colección sea segura para subprocesos es para mejorar el rendimiento de subprocesos múltiples. Ejemplo: ConcurrentHashMap sobre HashMap. Debido a que CHM internaliza las preocupaciones de múltiples subprocesos, puede bloquear por bandas para un mayor acceso concurrente de manera más efectiva que la sincronización externa.


Este es un buen comienzo.

thread-safe-dictionary

Pero notarás que pierdes una de las grandes características de las colecciones: la enumeración. No se puede subprocesar un enumerador, simplemente no es realmente factible, a menos que implemente su propio enumerador que retiene un bloqueo de instancia en la colección misma. Sospecho que esto causaría cuellos de botella y bloqueos potenciales.


Las colecciones seguras de hilos pueden engañar. Jared Par publicó un par de interesantes artículos sobre colecciones seguras para hilos:

El problema es que hay varios niveles de colecciones seguras para hilos. Encuentro que cuando la mayoría de la gente dice que la recolección es segura, lo que realmente quieren decir es "una colección que no se corromperá cuando se modifique y se acceda desde varios hilos"

...

Pero si la creación de una lista de seguridad de subprocesos de datos es tan fácil, ¿por qué Microsoft no agrega estas colecciones estándar en el marco?

Respuesta: ThreadSafeList es una clase virtualmente inutilizable porque el diseño lo lleva por el camino al código incorrecto.

Los defectos en este diseño no son aparentes hasta que examine cómo se usan comúnmente las listas. Por ejemplo, tome el siguiente código que intenta tomar el primer elemento fuera de la lista si hay uno.

static int GetFirstOrDefault(ThreadSafeList<int> list) { if (list.Count > 0) { return list[0]; } return 0; }

Este código es una condición de carrera clásica. Considere el caso donde solo hay un elemento> en la lista. Si otro hilo elimina ese elemento entre el enunciado if y el enunciado return, el enunciado return emitirá una excepción porque está tratando de acceder a un índice no válido en la lista. Aunque ThreadSafeList es seguro para hilos de datos, no hay nada que garantice la validez de un valor de retorno de una llamada en la siguiente llamada al mismo objeto

http://blogs.msdn.com/b/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

http://blogs.msdn.com/b/jaredpar/archive/2009/02/16/a-more-usable-thread-safe-collection.aspx


A partir de JDK 5, si necesita una colección segura para hilos, primero vería si una de las colecciones ya implementadas en java.util.concurrent funcionaría. Como señalan los autores de Java Concurrency In Practice (incluido el tipo que escribió la mayoría de las clases) implementarlas correctamente es muy difícil, especialmente si el rendimiento es importante.

Citando http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html

Colecciones concurrentes

Además de Colas, este paquete proporciona implementaciones de Colección diseñadas para su uso en contextos de multiproceso: ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, CopyOnWriteArrayList y CopyOnWriteArraySet. Cuando se espera que muchos subprocesos accedan a una colección dada, un ConcurrentHashMap normalmente es preferible a un HashMap sincronizado, y un ConcurrentSkipListMap es normalmente preferible a un TreeMap sincronizado. Una CopyOnWriteArrayList es preferible a una ArrayList sincronizada cuando la cantidad esperada de lecturas y recorridos excede en gran medida la cantidad de actualizaciones de una lista.