multithreading - que - threads
¿Cuándo es multi-threading no es una buena idea? (14)
Hace poco estuve trabajando en una aplicación que enviaba y recibía mensajes a través de Ethernet y Serial. Luego me encomendaron agregar el monitoreo de DIO discretos. Yo a través de,
"No hay razón para interrumpir el hilo principal que está involucrado en el procesamiento de mensajes, solo crearé otro hilo que monitoree DIO".
Esta decisión, sin embargo, resultó ser pobre . En ocasiones, el hilo principal se interrumpiría entre un mensaje serie Enviar y Recibir. Esta interrupción interrumpiría el tiempo y, por desgracia, los mensajes se perderían (para siempre).
Encontré otra manera de monitorear el DIO sin usar otro hilo y las comunicaciones Ethernet y en serie fueron restauradas a su funcionalidad correcta.
Todo el fiasco, sin embargo, me hizo pensar. ¿No son una buena idea sus pautas generales sobre cuándo no usar múltiples hilos y / o alguien más tiene más ejemplos de situaciones cuando se utilizan hilos múltiples?
** EDITAR: con base en sus comentarios y después de buscar en Internet información, he compuesto una publicación de blog titulada ¿ Cuándo es multi-threading no es una buena idea?
¿Los procesos son paralelos? ¿El rendimiento es una preocupación real? ¿Hay varios ''hilos'' de ejecución como en un servidor web? No creo que haya una respuesta finita.
El multi-threading no es una buena idea si necesita garantizar una sincronización física precisa (como en su ejemplo). Otros inconvenientes incluyen el intercambio de datos intensivo entre hilos. Diría que el multi-threading es bueno para tareas realmente paralelas si no te importa mucho su velocidad / prioridad / tiempo relativos.
En principio, cada vez no hay sobrecarga para que la persona que llama espere en una cola.
En realidad, el multi-threading no es escalable y es difícil de depurar, por lo que no debe usarse en ningún caso si puede evitarlo. Hay pocos casos en que sea obligatorio: cuando importa el rendimiento en una CPU múltiple, o cuando se trata con un servidor que tiene muchos clientes que tardan mucho tiempo en responder.
En cualquier otro caso, puede usar alternativas como los trabajos queue + cron u otros.
Es posible que desee echar un vistazo a la página web " The C10K problem " de Dan Kegel sobre el manejo de múltiples fuentes / sumideros de datos.
Básicamente, es mejor utilizar hilos mínimos, que en sockets se pueden hacer en la mayoría de sistemas operativos con algún sistema de eventos (o de forma asíncrona en Windows usando IOCP).
Cuando se encuentra con el caso donde el sistema operativo y / o las bibliotecas no ofrecen una manera de realizar la comunicación de forma no bloqueante, lo mejor es utilizar un grupo de subprocesos para manejarlos mientras se informa al mismo ciclo de eventos.
Diagrama de ejemplo de diseño:
Per CPU [*] EVENTLOOP ------ Handles nonblocking I/O using OS/library utilities
| /___ Threadpool for various blocking events
Threadpool for handling the I/O messages that would take long
Parafraseando una cita anterior: un programador tuvo un problema. Pensó: "Lo sé, usaré hilos". Ahora el programador tiene dos problemas. (A menudo atribuido a JWZ, pero parece anterior a su uso de él hablando de expresiones regulares).
Una buena regla empírica es "No usar subprocesos, a menos que haya una razón muy convincente para usar subprocesos". Múltiples hilos están pidiendo problemas. Trate de encontrar una buena manera de resolver el problema sin usar múltiples hilos, y solo recurra al uso de hilos si evitarlo es tan problemático como el esfuerzo extra de usar hilos. Además, considere cambiar a varios subprocesos si está ejecutando en una máquina multi-core / multi-CPU, y las pruebas de rendimiento de la versión de un solo subproceso muestran que necesita el rendimiento de los núcleos adicionales.
Un par de razones más posibles para usar hilos:
- Su plataforma carece de operaciones de E / S asíncronas, por ejemplo, Windows ME (sin puertos de terminación o E / S superpuestas, una molestia al portar aplicaciones de XP que las utilizan). Java 1.3 y versiones anteriores.
- Una función de biblioteca de terceros que puede bloquearse, por ejemplo, si un servidor remoto está inactivo, y la biblioteca no proporciona ninguna forma de cancelar la operación y usted no puede modificarla.
Mantener una GUI receptiva durante el procesamiento intensivo no siempre requiere hilos adicionales. Una sola función de devolución de llamada suele ser suficiente.
Si ninguno de los anteriores se aplica y, por alguna razón, sigo queriendo el paralelismo, prefiero iniciar un proceso independiente si es posible.
Una aplicación reciente que escribí que tenía que usar subprocesamiento múltiple (aunque no era un número ilimitado de subprocesos) era una en la que tenía que comunicarme en varias direcciones sobre dos protocolos, además de supervisar un tercer recurso para los cambios. Ambas bibliotecas de protocolo necesitaban un subproceso para ejecutar el respectivo bucle de evento y, cuando se contabilizaban, era fácil crear un tercer bucle para la supervisión de recursos. Además de los requisitos de bucle de eventos, los mensajes que pasaban por los cables tenían requisitos de temporización estrictos, y un bucle no podía arriesgarse a bloquear al otro, algo que se aliviaba aún más mediante el uso de una CPU multinúcleo (SPARC).
Hubo más discusiones sobre si cada procesamiento de mensajes debería considerarse un trabajo que se le dio a un hilo de un grupo de subprocesos, pero al final fue una extensión que no valía la pena.
Con todo, los subprocesos, si es posible, solo deben tenerse en cuenta cuando puede dividir el trabajo en trabajos bien definidos (o series de trabajos) de modo que la semántica sea relativamente fácil de documentar e implementar, y puede poner un límite superior en el cantidad de hilos que utiliza y que necesitan interactuar. Los sistemas donde esto se aplica mejor son casi sistemas de paso de mensajes.
Yo diría que multi-threading generalmente se usa para:
- Permitir el procesamiento de datos en segundo plano mientras una GUI sigue siendo receptiva
- Divida el análisis de datos muy grande en varias unidades de procesamiento para que pueda obtener sus resultados más rápido.
- Cuando recibe datos de algún hardware y necesita algo para agregarlo continuamente a un búfer mientras que otro elemento decide qué hacer con él (escribir en el disco, mostrarlo en una GUI, etc.).
Entonces, si no está resolviendo uno de esos problemas, es poco probable que agregar hilos le haga la vida más fácil. De hecho, casi seguramente lo hará más difícil porque, como otros han mencionado; la depuración de aplicaciones mutthreaded es considerablemente más trabajo que una solución de subproceso único.
La seguridad puede ser una razón para evitar el uso de múltiples hilos (en múltiples procesos). Consulte Google Chrome para ver un ejemplo de funciones de seguridad multiproceso.
Multi-threading es una mala idea si:
Varios subprocesos acceden y actualizan el mismo recurso (establecer una variable, escribir en un archivo), y no entiende la seguridad de subprocesos .
Varios hilos interactúan entre sí y no comprende mutexes y herramientas de administración de hilos similares.
Su programa usa variables estáticas (los subprocesos generalmente los comparten de forma predeterminada).
No ha depurado los problemas de simultaneidad.
El subprocesamiento múltiple es malo, excepto en el caso individual en el que es bueno. Este caso es
- El trabajo es CPU Bound, o partes de él es CPU Bound
- El trabajo es paralelizable
Si una o ambas de estas condiciones se pierden, el multihilo no será una estrategia ganadora.
Si el trabajo no está vinculado a la CPU, entonces está esperando que no haya subprocesos para finalizar el trabajo, sino más bien algún evento externo, como la actividad de la red, para que el proceso complete su trabajo. Al usar subprocesos, existe el costo adicional de los cambios de contexto entre subprocesos, el costo de sincronización (mutexes, etc.) y la irregularidad de prioridad de subprocesos. La alternativa en el uso más común es la IO asíncrona, en la cual un solo hilo escucha varios puertos y actúa sobre lo que esté listo ahora, uno a la vez. Si, por casualidad, todos estos canales lentos se preparan al mismo tiempo, puede parecer que experimentará una desaceleración, pero en la práctica esto rara vez es cierto. El costo de manejar cada puerto individualmente a menudo es comparable o mejor que el costo de sincronizar el estado en múltiples hilos a medida que se vacía cada canal.
Muchas tareas pueden estar unidas por cálculo, pero aún no es práctico utilizar un enfoque multiproceso porque el proceso debe sincronizarse en todo el estado. Tal programa no puede beneficiarse del multihilo porque no se puede realizar ningún trabajo al mismo tiempo. Afortunadamente, la mayoría de los programas que requieren enormes cantidades de CPU se pueden paralelizar a algún nivel.
Multi-threading es escalable y permitirá que su UI mantenga su capacidad de respuesta mientras hace cosas muy complicadas en segundo plano. No entiendo dónde otras respuestas están adquiriendo su información en multi-threading.
Cuando no debe multi-hilo es una pregunta errónea para su problema. Su problema es este: ¿por qué el multihelado de mi aplicación hace que las comunicaciones serie / ethernet fallen?
La respuesta a esa pregunta dependerá de la implementación, que debería discutirse en otra pregunta. Sé con certeza que puede tener comunicaciones en serie y de ethernet en una aplicación multiproceso al mismo tiempo que muchas otras tareas sin causar pérdida de datos.
La única razón para no usar multi-threading es:
- Hay una tarea y ninguna interfaz de usuario con la que la tarea pueda interferir.
Las razones para usar mutli-threading son:
- Proporciona una capacidad de respuesta superior al usuario
- Realiza múltiples tareas al mismo tiempo para disminuir el tiempo de ejecución general
- Utiliza más CPU actuales multi-core y multi-multi-cores del futuro.
Hay tres métodos básicos de programación de subprocesos múltiples que hacen que la seguridad de subprocesos se implemente con facilidad; solo necesita usar uno para tener éxito:
- Subprocesos Tipos de datos seguros pasados entre subprocesos.
- Enlazar métodos seguros en el objeto enhebrado para modificar los datos transmitidos.
- Capacidades de PostMessage para comunicarse entre hilos.
Una fuente común de problemas de subprocesos son los enfoques habituales empleados para sincronizar los datos. Tener hilos compartir el estado y luego implementar el bloqueo en todos los lugares apropiados es una gran fuente de complejidad para el diseño y la depuración. Obtener el bloqueo correcto para equilibrar la estabilidad, el rendimiento y la escalabilidad siempre es un problema difícil de resolver. Incluso los expertos más experimentados se equivocan con frecuencia. Las técnicas alternativas para tratar con el enhebrado pueden aliviar gran parte de esta complejidad. El lenguaje de programación Clojure implementa varias técnicas interesantes para manejar la concurrencia.
- En una máquina de procesador único y una aplicación de escritorio, utiliza varios hilos para que no se congele la aplicación, sino para nada más.
- En un único servidor de procesador y una aplicación basada en la web, no es necesario realizar varios subprocesos porque el servidor web se encarga de la mayor parte.
- En una máquina y una aplicación de escritorio multiprocesador, se le sugiere utilizar múltiples hilos y programación paralela. Crea tantos hilos como procesadores.
- En un servidor multiprocesador y una aplicación basada en la web, no es necesario volver a utilizar varios subprocesos porque el servidor web lo maneja.
En total, si usa varios subprocesos para aplicaciones de escritorio distintas a la congelación y cualquier otra respuesta genérica, hará que la aplicación sea más lenta si tiene una sola máquina central debido a que los hilos se interrumpen entre sí.
¿Por qué? Debido a los interruptores de hardware. Lleva tiempo que el hardware cambie entre hilos en total. En una caja de varios núcleos, avance y use 1 hilo por cada núcleo y verá una rampa en gran aumento.