programacion procesos patrones metodo interrupciones evitar concurrente concurrencia barreras asincronos java c++ c unit-testing concurrency

procesos - metodo join java



Unidades de prueba en tiempo real/software concurrente (6)

La mayor parte del trabajo que hago en estos días involucra sistemas multiproceso y / o distribuidos. La mayoría de los errores implican errores de tipo "sucede antes", donde el desarrollador asume (erróneamente) que el evento A siempre ocurrirá antes del evento B. Pero cada 1000000 veces que se ejecuta el programa, el evento B ocurre primero, y esto causa un comportamiento impredecible.

Además, realmente no hay buenas herramientas para detectar problemas de tiempo, ni siquiera la corrupción de datos causada por las condiciones de la carrera. Las herramientas como Helgrind y drd del kit de herramientas de Valgrind son excelentes para programas triviales, pero no son muy útiles para diagnosticar sistemas grandes y complejos. Por un lado, reportan falsos positivos con bastante frecuencia (especialmente Helgrind). Por otra parte, es difícil detectar ciertos errores mientras se ejecuta bajo Helgrind / drd simplemente porque los programas que se ejecutan bajo Helgrind se ejecutan casi 1000 veces más lento, y con frecuencia es necesario ejecutar un programa durante bastante tiempo para incluso reproducir la condición de carrera. Además, dado que ejecutarse bajo Helgrind cambia totalmente el tiempo del programa, puede ser imposible reproducir un problema de tiempo determinado. Ese es el problema con los problemas de tiempo sutiles; son casi heisenbergianos en el sentido de que alterar un programa para detectar problemas de tiempo puede oscurecer el problema original.

El hecho triste es que la raza humana aún no está preparada adecuadamente para lidiar con un software complejo y concurrente. Desafortunadamente, no hay una manera fácil de probarlo por unidad. Especialmente para sistemas distribuidos, debe planificar su programa cuidadosamente utilizando los diagramas de suceso de Lamport para ayudarlo a identificar el orden necesario de eventos en su programa. Pero en última instancia, realmente no se puede escapar de las pruebas de unidad de fuerza bruta con entradas que varían aleatoriamente. También ayuda a variar la frecuencia del cambio de contexto de subprocesos durante su prueba de unidad, por ejemplo, ejecutando otro proceso en segundo plano que solo ocupa ciclos de CPU. Además, si tiene acceso a un clúster, puede ejecutar varias pruebas unitarias en paralelo, lo que puede detectar errores mucho más rápido y ahorrarle mucho tiempo.

Posible duplicado:
¿Cómo debo hacer una prueba unitaria de código enhebrado?

La prueba de unidad clásica consiste básicamente en poner x dentro y esperar y salir, y automatizar ese proceso. Así que es bueno para probar cualquier cosa que no implique tiempo . Pero entonces, la mayoría de los errores no triviales que he encontrado han tenido algo que ver con el tiempo. Los hilos corrompen los datos de los demás o causan puntos muertos. Ocurre un comportamiento no determinista - en uno se queda sin millón. Cosas difíciles.

¿Hay algo útil por ahí para "pruebas unitarias" de partes de sistemas multiproceso y concurrentes? ¿Cómo funcionan estas pruebas? ¿No es necesario ejecutar el tema de dicha prueba durante mucho tiempo y variar el entorno de alguna manera inteligente, para estar razonablemente seguro de que funciona correctamente?


Nunca he oído hablar de nada que pueda.

Supongo que si alguien diseñara uno, tendría que tener un control exacto sobre la ejecución de los subprocesos y ejecutar todas las combinaciones posibles de pasos de los subprocesos.

Suena como una tarea importante, sin mencionar las combinaciones matemáticas para hilos de tamaño no trivial cuando hay un puñado o más de ellos ...

Aunque, una búsqueda rápida de ... ¿ Unidad de prueba de una aplicación multiproceso?


Si el sistema probado es lo suficientemente simple, podría controlar la concurrencia bastante bien bloqueando las operaciones en sistemas de maquetas externas. Este bloqueo se puede hacer, por ejemplo, esperando que se inicie alguna otra operación. Si puede controlar todas las llamadas externas, esto podría funcionar bastante bien implementando diferentes secuencias de bloqueo. He intentado esto y revela errores de nivel de bloqueo bastante bien si conoces bien las posibles secuencias problemáticas. Y en comparación con muchas otras pruebas de concurrencia, es bastante determinista. Sin embargo, este enfoque no detecta demasiado bien las condiciones de carrera de bajo nivel. Por lo general, solo hago pruebas de carga para encontrarlas, pero supongo que no es exactamente una prueba unitaria.

He visto estos marcos de prueba de concurrencia para .net, asumo que es solo cuestión de tiempo antes de que alguien escriba uno para Java (con suerte).

Y no olvidar la buena lectura de códigos antiguos. Una de las mejores maneras de encontrar errores de concurrencia es simplemente leer el código una vez más, dándole toda su concentración.


Si puede ejecutar sus pruebas en Linux, valgrind incluye una herramienta llamada helgrind que pretende detectar condiciones de carrera y posibles interbloqueos en programas que usan pthreads; puede obtener algún beneficio al ejecutar su código multiproceso bajo eso, ya que informará de posibles errores incluso si realmente no ocurrieron en esa ejecución de prueba en particular.


Tal vez la respuesta es que no deberías. En los sistemas concurrentes, puede que no siempre haya una sola respuesta determinista que sea correcta.

Tomemos el ejemplo de personas que suben a un tren y eligen un asiento. Vas a terminar con diferentes resultados cada vez.


Awaitility es un marco útil cuando necesita lidiar con la asincronicidad en sus pruebas. Le permite esperar hasta que se actualice algún estado de su sistema. Por ejemplo:

await().untilCall( to(myService).myMethod(), equalTo(3) );

o

await().until( fieldIn(myObject).ofType(int.class), greaterThan(1));

También tiene soporte Scala y Groovy.