Me he enfrentado a este problema varias veces en los últimos años al escribir código de manejo de hilos para varios proyectos. Proporciono una respuesta tardía porque la mayoría de las otras respuestas, aunque ofrecen alternativas, en realidad no responden la pregunta sobre las pruebas. Mi respuesta se dirige a los casos donde no hay alternativa al código multiproceso; Cubro los problemas de diseño del código para completarlos, pero también analizo las pruebas unitarias.
Escribir código multiproceso comprobable
Lo primero que debe hacer es separar su código de manejo de subprocesos de producción de todo el código que realiza el procesamiento de datos real. De esa manera, el procesamiento de datos se puede probar como código de subproceso único, y lo único que hace el código multiproceso es coordinar subprocesos.
La segunda cosa para recordar es que los errores en el código multiproceso son probabilísticos; Los errores que se manifiestan con menos frecuencia son los errores que se infiltrarán en la producción, serán difíciles de reproducir incluso en la producción y, por lo tanto, causarán los mayores problemas. Por esta razón, el enfoque de codificación estándar de escribir el código rápidamente y luego depurarlo hasta que funcione es una mala idea para el código multiproceso; dará como resultado un código donde se arreglan los errores fáciles y los errores peligrosos todavía están allí.
En cambio, al escribir código multiproceso, debe escribir el código con la actitud de que va a evitar escribir los errores en primer lugar. Si ha eliminado correctamente el código de procesamiento de datos, el código de manejo de subprocesos debe ser lo suficientemente pequeño, preferiblemente unas pocas líneas, en el peor de los casos, unas pocas docenas de líneas, de modo que tenga la posibilidad de escribirlo sin escribir un error, y ciertamente sin escribir muchos errores , si comprende el enhebrado, tómese su tiempo y tenga cuidado.
Escritura de pruebas unitarias para código multiproceso
Una vez que el código multiproceso se escribe con el mayor cuidado posible, todavía vale la pena escribir pruebas para ese código. El propósito principal de las pruebas no es tanto para detectar errores de condición de carrera altamente dependientes del tiempo, es imposible probar repetidamente tales condiciones de carrera, sino probar que su estrategia de bloqueo para prevenir tales errores permite que múltiples hilos interactúen según lo previsto .
Para probar correctamente el comportamiento correcto de bloqueo, una prueba debe iniciar varios subprocesos. Para que la prueba sea repetible, queremos que las interacciones entre los hilos sucedan en un orden predecible. No queremos sincronizar externamente los hilos en la prueba, porque eso enmascarará los errores que podrían ocurrir en la producción donde los hilos no están sincronizados externamente. Eso deja el uso de retrasos de sincronización para la sincronización de subprocesos, que es la técnica que he utilizado con éxito cada vez que tuve que escribir pruebas de código multiproceso.
Si los retrasos son demasiado cortos, entonces la prueba se vuelve frágil, porque las pequeñas diferencias de tiempo, por ejemplo, entre diferentes máquinas en las que se pueden ejecutar las pruebas, pueden hacer que el tiempo se desactive y la prueba falle. Lo que generalmente he hecho es comenzar con demoras que causan fallas en la prueba, aumentar las demoras para que la prueba pase de manera confiable en mi máquina de desarrollo y luego duplicar las demoras más allá de eso para que la prueba tenga una buena posibilidad de pasar en otras máquinas. Esto significa que la prueba tomará una cantidad de tiempo macroscópica, aunque en mi experiencia, el diseño cuidadoso de la prueba puede limitar ese tiempo a no más de una docena de segundos. Dado que no debería tener muchos lugares que requieran código de coordinación de subprocesos en su aplicación, eso debería ser aceptable para su conjunto de pruebas.
Finalmente, realice un seguimiento de la cantidad de errores detectados por su prueba. Si su prueba tiene una cobertura de código del 80%, se espera que detecte alrededor del 80% de sus errores. Si su prueba está bien diseñada pero no encuentra errores, existe una posibilidad razonable de que no tenga errores adicionales que solo aparecerán en la producción. Si la prueba detecta uno o dos errores, aún puede tener suerte. Más allá de eso, y es posible que desee considerar una revisión cuidadosa o incluso una reescritura completa de su código de manejo de subprocesos, ya que es probable que el código aún contenga errores ocultos que serán muy difíciles de encontrar hasta que el código esté en producción, y muy difícil de arreglar entonces.