2011-01-07 12 views
15

Hay muchos artículos y discusiones que explican por qué es bueno crear clases seguras para subprocesos. Se dice que si múltiples subprocesos tienen acceso, p. un campo al mismo tiempo, solo puede haber algunas malas consecuencias. Entonces, ¿cuál es el punto de mantener no código seguro para subprocesos? Me estoy enfocando principalmente en .NET, pero creo que las razones principales no dependen del idioma.¿Para qué no es seguro para hilos?

E.g. Los campos estáticos de .NET no son seguros para subprocesos. ¿Cuál sería el resultado si fuera seguro para subprocesos de forma predeterminada? (sin necesidad de realizar un bloqueo "manual"). ¿Cuáles son los beneficios de usar (en realidad, por defecto) seguridad sin hilos?

Una cosa que me viene a la mente es el rendimiento (más que una conjetura, sin embargo). Es bastante intuitivo que, cuando una función o campo no necesita ser seguro para subprocesos, no debería serlo. Sin embargo, la pregunta es: ¿para qué? ¿La seguridad de los hilos es solo una cantidad adicional de código que siempre debe implementar? En qué escenarios puedo estar 100% seguro de que, p. un campo no será utilizado por dos hilos a la vez?

Respuesta

15

escritura flujos seguros de código:

  1. requiere que los desarrolladores más calificados
  2. es más difícil y consume más esfuerzos de codificación
  3. es más difícil de probar y depurar
  4. Por lo general, tiene costo de rendimiento mayor

¡Pero! El código de seguridad no siempre es necesario. Si puede estar seguro de que se accederá a un fragmento de código con un solo hilo, la lista anterior se convierte en una sobrecarga enorme e innecesaria. Es como alquilar una camioneta cuando vas a la ciudad vecina cuando hay dos y no mucho equipaje.

+0

¿Pero cuándo definitivamente puedes decir que una DLL nunca usará threading? ¿Qué pasa con los clientes, a quienes les gustaría experimentar con su biblioteca? ¿Debería uno hacer que todos los métodos públicos sean seguros para subprocesos? En cuanto a la furgoneta: a algunas personas les gusta presumir. :) – pbalaga

+0

@rook: recuerde, si es una buena biblioteca no tiene que preocuparse por cómo implementa las cosas: nunca debe darle acceso a cosas que no son seguras a menos que las marque con mucho cuidado. – Crisfole

+3

@rook: con respecto a los clientes que experimentan con su biblioteca, a menos que esté documentando explícitamente que un método es seguro para subprocesos, es seguro asumir que no es así. Los clientes asumen la responsabilidad de mantener la seguridad de las secuencias al usar sus clases. – Juliet

11

La seguridad de subprocesos tiene sus costos: debe bloquear los campos que podrían causar problemas si se accede simultáneamente.

En las aplicaciones que no usan subprocesos pero que requieren un alto rendimiento cuando cada ciclo de CPU cuenta, no hay ninguna razón para tener clases de subprocesos seguros.

+0

¿Qué tipo de problemas son estos? ¿Qué sucede si dos hilos realizan solo operaciones de lectura en un campo? – pbalaga

+1

@rook: si dos hilos realizan operaciones de lectura en un campo, leerán el valor del campo. Ahora bien, si ambos hilos dicen que modifica el campo simultáneamente, es posible que tenga problemas. –

5

Entonces, ¿cuál es el punto de mantener el código no seguro para subprocesos?

Costo. Como suponía, generalmente hay una penalización en el rendimiento.

Además, la escritura de código seguro de subprocesos es más difícil y lleva más tiempo.

5

La seguridad del subproceso no es una proposición "sí" o "no". El significado de "seguridad de hilos" depende del contexto; ¿significa "lectura simultánea segura, escritura simultánea insegura"? ¿Significa que la aplicación podría devolver datos obsoletos en lugar de bloquearse? Hay muchas cosas que puede significar.

La razón principal para no hacer una clase "thread safe" es el costo. Si el tipo no será accedido por múltiples hilos, no hay ninguna ventaja al realizar el trabajo y aumentar el costo de mantenimiento.

3

Escribir el código threadsafe es muy difícil a veces. Por ejemplo, la carga lenta simple requiere dos comprobaciones para '== nulo' y un bloqueo. Es realmente fácil arruinarlo.

[EDIT]

Yo no quería decir que sugiera que enrosca la carga diferida era particularmente difícil, es el "Ah, y que no recordaba para bloquear esa primera!" momentos que llegan rápido y difícil una vez que piensas que has terminado con el bloqueo que realmente son el desafío.

+0

También es lento. Los caracteres deben ser volátiles para que el compilador no optimice el acceso. Volátil significa "cache flushes". Básicamente, el procesamiento de la CPU es mucho más lento. – TomTom

+0

@TomTom: absolutamente, solo estoy dando mi principal razón para no hacer las cosas en paralelo todo el tiempo. A su consumidor promedio no le preocupa demasiado la velocidad a menos que lo note, lo cual es bastante improbable para la mayoría de las aplicaciones actuales. – Crisfole

+0

¡Ah, qué bien!) Desearía tener este lujo, trabajar con MUCHA información o datos críticos en el tiempo cambian mi punto de vista. – TomTom

2

Existen situaciones donde "hilo seguro" no tiene sentido. Esta consideración se suma a la mayor habilidad del desarrollador y al aumento del tiempo (el desarrollo, las pruebas y el tiempo de ejecución todos reciben visitas).

Por ejemplo, List<T> es una clase comúnmente no usada para subprocesos. Si tuviéramos que crear un equivalente seguro para subprocesos, ¿cómo implementaríamos GetEnumerator? Sugerencia: no hay una buena solución.

+0

Sin embargo, todavía existen algunos. ¿Qué hay de malo con tales implementaciones? – pbalaga

+0

No estoy seguro de lo que quiere decir con "todavía existen algunos". La única forma de tener estructuras de datos verdaderamente seguras para subprocesos es hacerlas inmutables. –

+0

De hecho, "quieto" es golpear. Lo que quiero decir es que existen algunas soluciones, que parecen abordar el tema de seguridad de subprocesos de manera efectiva. Quizás no sean "buenos" como dices. – pbalaga

2

Entonces, ¿qué sentido tiene mantener el código no seguro para subprocesos?

Al permitir que el código no sea seguro para subprocesos, lo deja hasta el programador para decidir cuál es el nivel correcto de aislamiento.

Como han mencionado otros, esto permite una reducción de la complejidad y un rendimiento mejorado.

Rico Mariani escribió dos artículos titulados "Putting your synchronization at the correct level" y Putting your synchronization at the correct level -- solution que tienen un buen ejemplo de esto en acción.

En el artículo tiene un método llamado DoWork(). En él él llama a otras clases Read dos veces Write dos veces y luego LogToSteam.

Read, Write y LogToSteam todos compartieron un candado y estaban seguros para subprocesos. Esto es bueno, excepto por el hecho de que debido a que DoWork también era seguro para hilos, todo el trabajo de sincronización en cada Read, Write y LogToSteam fue una completa pérdida de tiempo.

Todo esto está relacionado con la naturaleza Imperative Programming. Sus efectos secundarios causan la necesidad de esto.

Sin embargo, si tuviera una plataforma de desarrollo donde las aplicaciones podrían expresarse como funciones puras donde no había dependencias o efectos secundarios, entonces sería posible crear aplicaciones donde el subprocesamiento se administró sin intervención del desarrollador.

0

So, what is the point of keeping non thread-safe code?

La regla de oro es para evitar el bloqueo tanto como sea posible. El código Ideal es reentrante y está seguro para hilos sin ningún bloqueo. Pero eso sería una utopía.

Volviendo a la realidad, un programador good intenta su mejor nivel para tener un bloqueo seccional en lugar de bloquear todo el contexto. Un ejemplo sería bloquear pocas líneas de código a la vez en varias rutinas que bloquear todo en una función.

Así también, uno tiene que refactorizar el código para obtener un diseño que minimice el bloqueo si no lo elimina en su totalidad.

p. Ej. considere una función foobar() que obtiene datos nuevos en cada llamada y usa switch() case en un tipo de datos para cambiar un nodo en un árbol. El bloqueo se puede evitar principalmente (si no completamente) ya que cada declaración de caso tocaría un nodo diferente en un árbol. Este puede ser un ejemplo más específico, pero creo que elabora mi punto.

2

Convierta esta pregunta en su cabeza.

En los primeros días de la programación no existía el código Thread-Safe porque no existía el concepto de subprocesos. Se inició un programa, luego se avanzó paso a paso hasta el final. ¿Eventos? ¿Que es eso? ¿Trapos? ¿Huh?

A medida que el hardware se hizo más poderoso, los conceptos de qué tipo de problemas se podían resolver con el software se volvieron más imaginativos y los desarrolladores más ambiciosos, la infraestructura del software se volvió más sofisticada. También se volvió mucho más pesado. Y aquí estamos hoy, con un ecosistema de software sofisticado, potente y en algunos casos innecesariamente alto, que incluye hilos y "seguridad de hilos".

Me doy cuenta de que la pregunta está dirigida más a los desarrolladores de aplicaciones que, por ejemplo, a los desarrolladores de firmware, pero al observar todo el bosque se ofrece información sobre cómo evolucionó ese árbol.

Cuestiones relacionadas