2009-02-11 6 views
10

¿Cuál es una buena forma de aprovechar TDD para eliminar el código seguro para subprocesos? Por ejemplo, decir que tengo un método de fábrica que utiliza la inicialización perezosa para crear una sola instancia de una clase, y devolverlo a partir de entonces:Uso de TDD para expulsar el código seguro para subprocesos

private TextLineEncoder textLineEncoder; 
... 
public ProtocolEncoder getEncoder() throws Exception { 
    if(textLineEncoder == null) 
     textLineEncoder = new TextLineEncoder(); 
    return textLineEncoder; 
} 

Ahora, quiero escribir una prueba de buena manera TDD que me obliga a hacer este código seguro para hilos. Específicamente, cuando dos hilos llaman a este método al mismo tiempo, no quiero crear dos instancias y descartar uno. Esto se hace fácilmente, pero ¿cómo puedo escribir una prueba que me obligue a hacerlo?

Estoy preguntando esto en Java, pero la respuesta debería ser más ampliamente aplicable.

+0

Gracias. Salió esta mañana mientras escribes una de esas fábricas. Ha aparecido antes cuando se discutía el mecanismo de bloqueo con doble verificación, pero eso fue antes de que SO estuviera presente. –

Respuesta

3

Es difícil, aunque posible, posiblemente más difícil de lo que vale. Las soluciones conocidas implican instrumentar el código bajo prueba. La discusión here, "Extreme Programming Challenge Fourteen" vale la pena examinar.

+0

¡Qué idea más intrigante! Y tal vez un aspecto que falla si se llama a TextLineEncoder() por segunda vez. Hmmm ... –

0

Justo fuera de mi cabeza ¿podría comparar las instancias devueltas para ver si realmente son la misma instancia o si son diferentes? Probablemente sea donde comenzaría con C#, me imagino que puede hacer lo mismo en java

+0

Suena como un comienzo útil. Es fácil escribir una prueba de ese tipo, más difícil, sin embargo, escribirla para que falle debido a la falta de sincronización del hilo. – Morendil

+0

Creo que es un buen comienzo, pero si se produce un cambio de subproceso entre la construcción y la devolución, podrían crear dos instancias y aún así devolver la misma instancia. –

+0

esto es cierto. deberías asegurarte de que tienes los bloqueos apropiados alrededor del sistema bajo prueba –

1

El capítulo 12 de Java Concurrency in Practice se llama "Prueba de programas concurrentes". Documenta las pruebas de seguridad y vitalidad, pero dice que este es un tema difícil. No estoy seguro de que este problema se pueda resolver con las herramientas de ese capítulo.

5

Usted podría inyectar un "proveedor" (una fábrica muy simple) que es responsable por sólo esta línea:

textLineEncoder = new TextLineEncoder(); 

A continuación, la prueba sería inyectar una aplicación muy lento del proveedor. De esa forma, los dos hilos en la prueba podrían colisionar más fácilmente. Podrías llegar hasta el primer hilo esperando en un semáforo que sería liberado por el segundo hilo. Entonces, el éxito de la prueba aseguraría que el hilo de espera caduque. Al darle al primer hilo una ventaja, puede asegurarse de que está esperando antes de que se publique el segundo hilo.

+1

+1 Me gusta esto. Un constructor de TextLineEncoder realmente lento garantizaría virtualmente que surja el problema de concurrencia aquí. Una implementación ITextLineEncoderProvider podría simular fácilmente eso. –

+0

Y, como beneficio adicional, puede obtener flexibilidad y extensibilidad (puede usar diferentes codificadores de líneas de texto) y pruebas más enfocadas (puede usar la simulación TextLineEncoder). –

+1

Buena idea ... E incluso puede implementarse sin usar una fábrica lenta para 'TextLineEncoder', usando una herramienta de burla que admite la burla de constructores: en la primera invocación al constructor (primer hilo), simplemente bloquearía ; luego, si el segundo hilo entra y ejecuta el constructor por segunda vez, la prueba fallaría. –

2

En el libro Clean Code hay algunos consejos sobre cómo probar el código concurrente. Un consejo que me ha ayudado a encontrar errores de concurrencia, se está ejecutando al mismo tiempo más pruebas que la CPU tiene núcleos.

En my project, ejecutar las pruebas lleva alrededor de 2 segundos en mi máquina de cuatro núcleos. Cuando quiero probar las partes concurrentes (hay algunas pruebas para eso), mantengo apretado en IntelliJ IDEA la tecla rápida para ejecutar todas las pruebas, hasta que veo en la barra de estado que 20, 50 o 100 ejecuciones de prueba están en ejecución. Sigo en el Administrador de tareas de Windows la CPU y el uso de la memoria, para ver cuándo terminaron todas las ejecuciones de prueba (el uso de la memoria aumenta en 1-2 GB cuando se están ejecutando y luego baja lentamente).

A continuación cierro uno por uno todos los diálogos de salida de ejecución de prueba, y compruebo que no haya fallas. A veces hay pruebas o pruebas fallidas que están en punto muerto, y luego las investigo hasta que encuentre el error y lo haya arreglado. Eso me ha ayudado a encontrar un par de desagradables errores de concurrencia. Lo más importante, cuando se enfrenta a una excepción/interbloqueo que no debería haber sucedido, siempre es asumir que el código está roto, e investigar la razón sin piedad y solucionarlo. No hay rayos cósmicos que provoquen que los programas se bloqueen aleatoriamente: los errores en el código hacen que los programas fallen.

También hay marcos como http://www.alphaworks.ibm.com/tech/contest que utilizan la manipulación de bytecode para obligar al código a hacer más conmutación de subprocesos, lo que aumenta la probabilidad de que los errores de concurrencia sean visibles.

2

Cuando realicé una implementación que necesitaba ser segura para subprocesos recientemente, se me ocurrió la solución que proporcioné como respuesta para this question. Espero que ayude a pesar de que no hay pruebas allí. Espero que el enlace esté bien antes de duplicar la respuesta ...

+0

Sí, un enlace está bien. Realmente me gusta tu respuesta; es la primera vez que escucho que tiene una sensación de rectitud al respecto. Tal vez porque muchos de los otros se basan en un comportamiento no determinista. Particularmente convincente es su comentario de que "En primer lugar, su clase existente tiene una responsabilidad y es proporcionar cierta funcionalidad". Un objeto que conoce su seguridad de hilo es análogo a saber que está en una lista enlazada o en un contenedor web. Sabe dónde vive, por así decirlo, y para los objetos, eso no es bueno. Tendré que probar tu enfoque y ver cómo funciona. –

Cuestiones relacionadas