¿Qué lecciones aprendiste de un proyecto que casi / realmente fracasó debido a un mal subproceso múltiple?
A veces, el marco impone un cierto modelo de subprocesos que hace que las cosas en un orden de magnitud sean más difíciles de corregir.
En cuanto a mí, aún no me he recuperado del último fracaso y creo que es mejor para mí no trabajar en nada que tenga que ver con el subprocesamiento múltiple en ese marco.
Descubrí que era bueno en problemas de subprocesos múltiples que tienen una bifurcación / unión simple, y donde los datos solo viajan en una dirección (mientras que las señales pueden viajar en una dirección circular).
No puedo manejar la GUI en la que parte del trabajo solo se puede realizar en un subproceso estrictamente serializado (el "subproceso principal") y otro trabajo solo se puede realizar en cualquier subproceso que no sea el subproceso principal (los "subprocesos de trabajo") y donde los datos y los mensajes tienen que viajar en todas las direcciones entre N componentes (un gráfico completamente conectado).
En el momento en que dejé ese proyecto por otro, había problemas de punto muerto en todas partes. Escuché que 2-3 meses después, varios otros desarrolladores lograron solucionar todos los problemas de punto muerto, hasta el punto de que se pueden enviar a los clientes. Nunca logré descubrir ese conocimiento faltante que me falta.
Algo sobre el proyecto: el número de ID de mensajes (valores enteros que describen el significado de un evento que se puede enviar a la cola de mensajes de otro objeto, independientemente de la secuencia) se encuentra en varios miles. Las cadenas únicas (mensajes de usuario) también se encuentran en aproximadamente mil.
Adicional
La mejor analogía que obtuve de otro equipo (no relacionado con mis proyectos pasados o presentes) fue "poner los datos en una base de datos". ("Base de datos" que se refiere a centralización y actualizaciones atómicas). En una GUI que está fragmentada en múltiples vistas, todas se ejecutan en el mismo "hilo principal" y todo el trabajo pesado no GUI se realiza en hilos de trabajo individuales, los datos de la aplicación deben almacenarse en una sola placa que actúa como una base de datos y dejar que la "base de datos" maneje todas las "actualizaciones atómicas" que involucran dependencias de datos no triviales. Todas las otras partes de la GUI solo manejan el dibujo de la pantalla y nada más. Las partes de la interfaz de usuario podrían almacenar cosas en caché y el usuario no se dará cuenta si está obsoleto por una fracción de segundo, si está diseñado correctamente. Esta "base de datos" también se conoce como "el documento" en la arquitectura de vista de documento. Desafortunadamente, no, mi aplicación realmente almacena todos los datos en las Vistas. No sé por qué fue así.
Compañeros contribuyentes:
(los contribuyentes no necesitan usar ejemplos reales / personales. Las lecciones de ejemplos anecdóticos, si usted lo considera creíble, también son bienvenidas).
Respuestas:
Mi lección favorita: ¡muy ganada! - es que en un programa multiproceso el programador es un cerdo astuto que te odia. Si las cosas pueden salir mal, lo harán, pero de manera inesperada. Si te equivocas, estarás persiguiendo heisenbugs extraños (porque cualquier instrumentación que agregues cambiará los tiempos y te dará un patrón de ejecución diferente).
La única forma sensata de solucionar esto es acorralar estrictamente todo el manejo de hilos en un código tan pequeño que lo haga todo bien y que sea muy conservador para garantizar que los bloqueos se mantengan correctamente (y con un orden de adquisición globalmente constante también) . La forma más fácil de hacerlo es no compartir memoria (u otros recursos) entre subprocesos, excepto los mensajes que deben ser asíncronos; eso te permite escribir todo lo demás en un estilo que no tiene hilos. (Bonificación: escalar a varias máquinas en un clúster es mucho más fácil).
fuente
is that in a multithreaded program the scheduler is a sneaky swine that hates you.
- no, no hace exactamente lo que le dijiste que hiciera :)Aquí hay algunas lecciones básicas que puedo pensar en este momento (no por proyectos que fallan, sino por problemas reales vistos en proyectos reales):
fuente
Heredamos una parte donde el proyecto GUI está usando una docena de hilos. No está dando nada más que problemas. Puntos muertos, problemas de carrera, llamadas GUI de hilos cruzados ...
fuente
Java 5 y versiones posteriores tienen Ejecutores que están destinados a hacer la vida más fácil para manejar programas de estilo fork-join de subprocesos múltiples.
Úselos, eliminará mucho dolor.
(y, sí, esto lo aprendí de un proyecto :))
fuente
Tengo experiencia en sistemas embebidos en tiempo real. No puede probar la ausencia de problemas causados por subprocesos múltiples. (A veces puedes confirmar la presencia). El código tiene que ser demostrablemente correcto. Por lo tanto, las mejores prácticas en torno a cualquier interacción de subprocesos
fuente
Una analogía de una clase sobre multihilo que tomé el año pasado fue muy útil. La sincronización de subprocesos es como una señal de tráfico que protege una intersección (datos) del uso de dos automóviles (subprocesos) a la vez. El error que muchos desarrolladores cometen es encender las luces rojas en la mayor parte de la ciudad para dejar pasar un automóvil porque piensan que es demasiado difícil o peligroso descubrir la señal exacta que necesitan. Eso podría funcionar bien cuando el tráfico es escaso, pero conducirá al estancamiento a medida que su aplicación crezca.
Eso es algo que ya sabía en teoría, pero después de esa clase la analogía realmente me quedó grabada, y me sorprendió la frecuencia con la que investigaría un problema de subprocesos y encontraría una cola gigante, o interrumpiría la desactivación en todas partes durante una escritura en una variable solo se usaron dos subprocesos, o se mantuvieron mutexes durante mucho tiempo cuando se podría refactorizar para evitarlo por completo.
En otras palabras, algunos de los peores problemas de subprocesos son causados por un exceso al tratar de evitar problemas de subprocesos.
fuente
Intenta hacerlo de nuevo.
Al menos para mí, lo que creó una diferencia fue la práctica. Después de hacer un trabajo multiproceso y distribuido bastantes veces, simplemente le acostumbras.
Creo que la depuración es realmente lo que lo hace difícil. Puedo depurar código de subprocesos múltiples usando VS, pero realmente estoy completamente perdido si tengo que usar gdb. Mi culpa, probablemente.
Otra cosa sobre la que está aprendiendo más es sobre estructuras de datos sin bloqueo.
Creo que esta pregunta se puede mejorar realmente si especifica el marco. Los grupos de subprocesos de .NET y los trabajadores en segundo plano son realmente diferentes a QThread, por ejemplo. Siempre hay algunas trampas específicas de la plataforma.
fuente
He aprendido que las devoluciones de llamada de módulos de nivel inferior a módulos de nivel superior son un gran mal porque provocan la adquisición de bloqueos en un orden opuesto.
fuente