multithreading - ¿Cómo detectar y solucionar problemas de multihilo?
debugging language-agnostic (16)
A veces, las soluciones multiproceso no se pueden evitar. Si hay un error, necesita ser investigado en tiempo real, lo cual es casi imposible con la mayoría de las herramientas como Visual Studio. La única solución práctica es escribir trazas, aunque el rastreo mismo debería:
- no agregar ningún retraso
- no use ningún bloqueo
- ser multihilo seguro
- rastrear lo que sucedió en la secuencia correcta.
Esto suena como una tarea imposible, pero se puede lograr fácilmente escribiendo la traza en la memoria. En C #, se vería algo como esto:
public const int MaxMessages = 0x100;
string[] messages = new string[MaxMessages];
int messagesIndex = -1;
public void Trace(string message) {
int thisIndex = Interlocked.Increment(ref messagesIndex);
messages[thisIndex] = message;
}
El método Trace () es seguro para subprocesos múltiples, no bloquea y se puede invocar desde cualquier subproceso. En mi PC, lleva unos 2 microsegundos ejecutar, lo que debería ser lo suficientemente rápido.
Agregue instrucciones de Trace () donde crea que algo podría ir mal, deje que el programa se ejecute, espere hasta que ocurra el error, detenga el rastreo y luego investigue el rastreo para detectar cualquier error.
Una descripción más detallada para este enfoque que también recopila información sobre el hilo y el tiempo, recicla el búfer y muestra el rastro muy bien que puede encontrar en: CodeProject: Depuración de código multiproceso en tiempo real 1
Este es un seguimiento de esta pregunta , donde no obtuve ninguna información sobre este punto. Aquí está la breve pregunta:
¿Es posible detectar y depurar problemas provenientes del código de subprocesos múltiples?
A menudo tenemos que decirle a nuestros clientes: "No podemos reproducir el problema aquí, así que no podemos solucionarlo. Díganos los pasos para reproducir el problema, luego lo arreglaremos". Es una respuesta desagradable si sé que es un problema de subprocesos múltiples, pero la mayoría no lo hago. ¿Cómo puedo saber que un problema es un problema de subprocesos múltiples y cómo depurarlo?
Me gustaría saber si hay marcos de registro especiales, o técnicas de depuración, o inspectores de código, o cualquier otra cosa para ayudar a resolver estos problemas. Los enfoques generales son bienvenidos. Si alguna respuesta debe estar relacionada con el idioma, guárdela en .NET y Java.
Además de las otras buenas respuestas que ya obtienes: prueba siempre en una máquina con al menos tantos procesadores / núcleos de procesador como los usa el cliente, o como hay hilos activos en tu programa. De lo contrario, algunos errores de subprocesamiento múltiple pueden ser difíciles o imposibles de reproducir.
Además de los volcados de emergencia, una técnica es un extenso registro en tiempo de ejecución: donde cada hilo registra lo que está haciendo.
La primera pregunta cuando se informa un error, entonces, podría ser "¿Dónde está el archivo de registro?"
A veces puede ver el problema en el archivo de registro: "Este hilo está detectando un estado ilegal / inesperado aquí ... y mire, este otro hilo estaba haciendo eso, justo antes y / o después de esto".
Si el archivo de registro no dice lo que está sucediendo, discúlpese con el cliente, agregue lo suficiente, muchas declaraciones de registro adicionales al código, proporcione el nuevo código al cliente y diga que lo arreglará después de que ocurra una vez más. .
Desarrolle el código de la forma que Princess recomienda para su otra pregunta (objetos inmutables y mensajes de estilo Erlang). Será más fácil detectar problemas de subprocesamiento múltiple, porque las interacciones entre los subprocesos estarán bien definidas.
Estoy usando GNU y uso script simple
$ más gdb_tracer
b func.cpp:2871
r
#c
while (1)
next
#step
end
Lo mejor que puedo pensar es alejarme del código de subprocesos múltiples siempre que sea posible. Parece que hay muy pocos programadores que puedan escribir aplicaciones multihilo libres de errores y yo diría que no hay codificadores que puedan escribir aplicaciones grandes y libres de errores múltiples.
Me enfrenté a un problema de subproceso que daba MISMO resultado incorrecto y no se comportaba de manera imprevisible ya que cada vez otras condiciones (memoria, programador, carga de procesamiento) eran más o menos las mismas.
Desde mi experiencia, puedo decir que LO MÁS DIFÍCIL es reconocer que se trata de un problema de hilo, y MEJOR SOLUCIÓN es revisar el código de subprocesos múltiples cuidadosamente. Simplemente mirando cuidadosamente el código de la secuencia, debe intentar averiguar qué puede salir mal. Otras formas (tirada de hilo, perfilador, etc.) serán las segundas.
Para Java hay una herramienta de verificación llamada javapathfinder que me parece útil para depurar y verificar la aplicación multi-threading contra la posible condición de carrera y los errores de bloqueo de la muerte del código.
Funciona muy bien con Eclipse y Netbean IDE.
Pensé que la answer que obtuviste a tu otra pregunta era bastante buena. Pero enfatizaré estos puntos.
Solo modifica el estado compartido en una sección crítica (Exclusión mutua)
Adquiera cerraduras en un orden establecido y libérelas en el orden opuesto.
Utilice abstracciones preconstruidas siempre que sea posible (como las cosas en java.util.concurrent)
Además, algunas herramientas de análisis pueden detectar algunos problemas potenciales. Por ejemplo, FindBugs puede encontrar algunos problemas de subprocesamiento en programas Java. Tales herramientas no pueden encontrar todos los problemas (no son balas de plata) pero pueden ayudar.
Como vanslly en un comentario de esta respuesta, estudiar la salida de registro bien ubicada también puede ser muy útil, pero ten cuidado con Heisenbugs .
Suponiendo que tengo informes de problemas que son difíciles de reproducir, siempre los encuentro leyendo códigos, preferiblemente lectura de códigos de pares, para que pueda analizar las necesidades de subprocesamiento semántico / bloqueo. Cuando hacemos esto en base a un problema informado , me parece que siempre identificamos uno o más problemas con bastante rapidez. Creo que también es una técnica bastante barata para resolver problemas difíciles.
Perdón por no poder decirle que presione ctrl + shift + f13, pero no creo que haya nada de eso disponible. Pero el solo hecho de pensar en lo que el problema reportado en realidad da un sentido bastante fuerte de dirección en el código, por lo que no tiene que comenzar en main ().
Una pequeña tabla con algunas técnicas de depuración para tener en cuenta al depurar código multiproceso. El gráfico está creciendo, deje comentarios y sugerencias para agregar. (archivo de actualización en este enlace )
Visual Studio le permite inspeccionar la pila de llamadas de cada hilo y puede cambiar entre ellas. De ninguna manera es suficiente para rastrear todo tipo de problemas de enhebrado, pero es un comienzo. Se planean muchas mejoras para la depuración de subprocesos múltiples para el próximo VS2010.
He usado WinDbg + SoS para enhebrar problemas en el código .NET. Puede inspeccionar bloqueos (blokcs de sincronización), pilas de llamadas de subprocesos, etc.
assert () es tu amigo para detectar condiciones de carrera. Siempre que ingrese a una sección crítica, afirme que la invariante asociada con ella es verdadera (para eso están las CS). Aunque, lamentablemente, el cheque puede ser costoso y, por lo tanto, no adecuado para su uso en el entorno de producción.
El blog de Tess Ferrandez tiene buenos ejemplos del uso de WinDbg para depurar interbloqueos en .NET.
Los problemas de subprocesamiento / concurrencia son notablemente difíciles de replicar, que es una de las razones por las que debe diseñar para evitar o al menos minimizar las probabilidades. Esta es la razón por la cual los objetos inmutables son tan valiosos. Intente aislar objetos mutables en un solo hilo y luego controle cuidadosamente el intercambio de objetos mutables entre hilos. Intente programar con un diseño de entrega de objetos en lugar de objetos "compartidos". Para este último, utilice objetos de control totalmente sincronizados (que son más fáciles de razonar), y evite que un objeto sincronizado utilice otros objetos que también deben estar sincronizados, es decir, intente mantenerlos autónomos. Tu mejor defensa es un buen diseño.
Los bloqueos son los más fáciles de depurar, si puede obtener un seguimiento de la pila cuando está bloqueado. Dada la traza, la mayoría de los cuales detectan interbloqueos, es fácil precisar el motivo y luego razonar sobre el motivo y cómo solucionarlo. Con interbloqueos, siempre va a ser un problema adquirir los mismos bloqueos en diferentes órdenes.
Los bloqueos en vivo son más difíciles: poder observar el sistema mientras estás en el estado de error es tu mejor opción.
Las condiciones de carrera tienden a ser extremadamente difíciles de replicar, y son aún más difíciles de identificar a partir de la revisión manual del código. Con estos, el camino que suelo tomar, además de las extensas pruebas para replicar, es razonar sobre las posibilidades e intentar registrar información para probar o refutar teorías. Si tiene evidencia directa de corrupción estatal, es posible que pueda razonar sobre las posibles causas basadas en la corrupción.
Cuanto más complejo es el sistema, más difícil es encontrar errores de concurrencia y razonar sobre su comportamiento. Haga uso de herramientas como JVisualVM y los perfiles de conexión remota: pueden ser un salvavidas si puede conectarse a un sistema en estado de error e inspeccionar los hilos y objetos.
Además, tenga cuidado con las diferencias en el comportamiento posible que dependen del número de núcleos de CPU, tuberías, ancho de banda del bus, etc. Los cambios en el hardware pueden afectar su capacidad de replicar el problema. Algunos problemas solo se mostrarán en otros CPU de un solo núcleo solo en núcleos múltiples.
Una última cosa, trate de usar objetos de concurrencia distribuidos con las bibliotecas del sistema, por ejemplo, en Java java.util.concurrent
es su amigo. Escribir sus propios objetos de control de concurrencia es difícil y plagado de peligros; déjalo en manos de los expertos, si tienes opción.