Cómo explicar por qué el subprocesamiento múltiple es difícil

83

Soy un programador bastante bueno, mi jefe también es un programador bastante bueno. Aunque parece subestimar algunas tareas, como los subprocesos múltiples y lo difícil que puede ser (me resulta muy difícil para algo más que ejecutar algunos subprocesos, esperar a que todos terminen y luego devolver los resultados).

En el momento en que empiezas a tener que preocuparte por los puntos muertos y las condiciones de la carrera, me resulta muy difícil, pero el jefe no parece apreciar esto, creo que nunca se ha encontrado con esto. Basta con golpearlo para bloquearlo. Es más o menos la actitud.

Entonces, ¿cómo puedo presentarlo o explicar por qué podría estar subestimando las complejidades de la concurrencia, el paralelismo y los subprocesos múltiples? O tal vez estoy equivocado?

Editar: Solo un poco sobre lo que ha hecho, recorre una lista, para cada elemento de esa lista, cree un subproceso que ejecute un comando de actualización de base de datos basado en la información de ese elemento. No estoy seguro de cómo controló la cantidad de subprocesos ejecutados a la vez, supongo que debe haberlos añadido a una cola si hubiera demasiados ejecutándose (no habría usado un semáforo).

    
pregunta Mr Shoubs 02.06.2011 - 11:18
fuente

10 respuestas

29
  1. Si puedes contar con cualquier experiencia matemática, ilustra cómo un flujo de ejecución normal que es esencialmente determinista se convierte no solo en no determinista con varios hilos, sino en exponencialmente complejo, porque debes asegurarte Cada posible intercalación de instrucciones de la máquina seguirá haciendo lo correcto. Un simple ejemplo de una actualización perdida o una situación de lectura sucia es a menudo revelador.

  2. "Golpee un candado en él" es la solución trivial ... resuelve todos sus problemas si no le preocupa el rendimiento. ¡Intente ilustrar qué tan grande sería el impacto de un rendimiento si, por ejemplo, Amazon tuviera que bloquear toda la costa este cada vez que alguien en Atlanta ordena un libro!

respondido por el Kilian Foth 02.06.2011 - 11:32
fuente
79

El subproceso múltiple es simple. Codificar una aplicación para subprocesos múltiples es muy, muy fácil.

Hay un truco simple, y esto es usar una cola de mensajes bien diseñada (no no enrolle la suya propia) para pasar datos entre los subprocesos.

La parte difícil es intentar que varios subprocesos actualicen mágicamente un objeto compartido de alguna manera. Ahí es cuando se vuelve propenso a errores porque la gente no presta atención a las condiciones de la carrera que están presentes.

Muchas personas no utilizan las colas de mensajes e intentan actualizar los objetos compartidos y crean problemas por sí mismos.

Lo que se vuelve difícil es diseñar un algoritmo que funcione bien al pasar datos entre varias colas. Eso es difícil. Pero la mecánica de los hilos coexistentes (a través de colas compartidas) es fácil.

Además, tenga en cuenta que los subprocesos comparten recursos de E / S. Es poco probable que un programa enlazado de E / S (es decir, conexiones de red, operaciones de archivos u operaciones de base de datos) vaya más rápido con muchos subprocesos.

Si desea ilustrar el problema de actualización de objetos compartidos, eso es simple. Siéntate en la mesa con un montón de tarjetas de papel. Escriba un simple conjunto de cálculos (4 o 6 fórmulas simples) con mucho espacio en la página.

Aquí está el juego. Cada uno de ustedes lee una fórmula, escribe una respuesta y coloca una tarjeta con la respuesta.

Cada uno de ustedes hará la mitad del trabajo, ¿verdad? Has terminado en la mitad del tiempo, ¿no?

Si su jefe no piensa mucho y simplemente comienza, terminará en conflicto de alguna manera y ambos escribirán respuestas a la misma fórmula. Eso no funcionó porque hay una condición de raza inherente entre los dos que leen antes de escribir. Nada te impide leer la misma fórmula y sobrescribir las respuestas de cada uno.

Hay muchas, muchas formas de crear condiciones de carrera con recursos bloqueados o mal bloqueados.

Si desea evitar todos los conflictos, corte el papel en una pila de fórmulas. Sacas uno de la cola, escribes la respuesta y publicas las respuestas. No hay conflictos porque ambos leen de una cola de mensajes de un solo lector.

    
respondido por el S.Lott 02.06.2011 - 12:17
fuente
25

La programación multiproceso es probablemente la solución más difícil para la concurrencia. Básicamente es una abstracción de bajo nivel de lo que realmente hace la máquina.

Hay varios enfoques, como el modelo de actor o (software) memoria transaccional , que son mucho más fáciles. O trabajando con estructuras de datos inmutables (como listas y árboles).

En general, una separación adecuada de preocupaciones hace que los subprocesos múltiples sean más fáciles. Algo, a menudo olvidado, cuando las personas generan 20 hilos, todos intentan procesar el mismo búfer. Utilice reactores donde necesite sincronización y generalmente pase datos entre diferentes trabajadores con colas de mensajes.
Si tiene un bloqueo en la lógica de su aplicación, hizo algo mal.

Así que sí, técnicamente, los subprocesos múltiples son difíciles.
"Golpearlo con un candado en él" es prácticamente la solución menos escalable para problemas de concurrencia y, en realidad, anula todo el propósito de los subprocesos múltiples. Lo que hace es revertir un problema a un modelo de ejecución no concurrente. Cuanto más lo haga, más probable será que tenga solo un hilo en ejecución en ese momento (o 0 en un interbloqueo). Vence todo el propósito.
Esto es como decir: "Resolver los problemas del tercer mundo es fácil. Simplemente arroja una bomba sobre él". Solo porque hay una solución trivial, esto no hace que el problema sea trivial, ya que se preocupa por la calidad del resultado.

Pero en la práctica, resolver estos problemas es tan difícil como cualquier otro problema de programación y es mejor hacerlo con las abstracciones apropiadas. Lo que lo hace bastante fácil de hecho.

    
respondido por el back2dos 02.06.2011 - 12:21
fuente
14

Creo que hay un ángulo no técnico para esta pregunta: OMI es un tema de confianza. Por lo general, se nos pide que reproduzcamos aplicaciones complejas como, oh, no sé, Facebook, por ejemplo. Llegué a la conclusión de que si tiene que explicar la complejidad de una tarea a los no iniciados / gerentes, entonces algo está mal en Dinamarca.

Incluso si otros programadores ninja pudieran realizar la tarea en 5 minutos, sus estimaciones se basan en su capacidad personal. Su interlocutor debe aprender a confiar en su opinión sobre el asunto o contratar a alguien cuya palabra esté dispuesto a aceptar.

El desafío no radica en transmitir las implicaciones técnicas, que las personas tienden a ignorar o son incapaces de comprender a través de la conversación, sino en establecer una relación de respeto mutuo.

    
respondido por el sunwukung 02.06.2011 - 13:03
fuente
6

Un simple experimento mental para comprender los puntos muertos es el problema del filósofo gastronómico . Uno de los ejemplos que tiendo a usar para describir lo malas que pueden ser las condiciones de la carrera es la situación Therac 25 .

"Solo golpearlo" es la mentalidad de alguien que no se ha topado con errores difíciles con subprocesos múltiples. Y es posible que he piense que está exagerando la seriedad de la situación (no es así, es posible hacer estallar cosas o matar a personas con problemas de condición de raza, especialmente con software integrado que termina en los coches).

    
respondido por el Tangurena 02.06.2011 - 16:32
fuente
3

Las aplicaciones concurrentes no son deterministas. Con la cantidad excepcionalmente pequeña de código general que el programador ha reconocido como vulnerable, no controla cuando una parte de un subproceso / proceso se ejecuta en relación con cualquier parte de otro subproceso. La prueba es más difícil, lleva más tiempo y es poco probable que encuentre todos los defectos relacionados con la concurrencia. Los defectos, si se encuentran, son a menudo sutiles, por lo que no se pueden reproducir de manera consistente, por lo tanto, la reparación es difícil

Por lo tanto, la única aplicación concurrente correcta es una que sea demostrablemente correcta, algo que no se practica a menudo en el desarrollo de software. Como resultado, la respuesta de S.Lot es el mejor consejo general, ya que el paso de mensajes es relativamente fácil de probar que es correcto.

    
respondido por el mattnz 02.06.2011 - 23:07
fuente
3

Respuesta corta en dos palabras: NOETERMINISMO OBSERVABLE

Respuesta larga: Depende del enfoque de la programación concurrente que utilice, dado su problema. En el libro Conceptos, técnicas y modelos de programación de computadoras , los autores explican claramente Cuatro enfoques prácticos principales para escribir programas concurrentes:

  • Programación secuencial : un enfoque de línea de base que no tiene concurrencia;
  • concurrencia declarativa : se puede utilizar cuando no hay un indeterminismo observable;
  • concurrencia de paso de mensajes : paso concurrente de mensajes entre muchas entidades, donde cada entidad procesa el mensaje de forma interna de forma secuencial;
  • Concurrencia de estado compartido : subproceso que actualiza objetos pasivos compartidos por medio de acciones atómicas de grano grueso, por ejemplo. cerraduras, monitores y transacciones;

Ahora, el más fácil de estos cuatro enfoques, aparte de la programación secuencial obvia, es concurrencia declarativa , ya que los programas escritos que utilizan este enfoque no tienen detectives no determinantes . En otras palabras, no hay condiciones de raza , ya que la condición de raza es simplemente un comportamiento no determinista observable.

Pero la falta de un no determinismo observable significa que existen algunos problemas que no podemos resolver mediante el uso de la concurrencia declarativa. Aquí es donde entran en juego los dos últimos enfoques no tan fáciles. La parte no tan fácil es una consecuencia del no determinismo observable. Ahora ambos caen bajo un modelo concurrente con estado y también son equivalentes en expresividad. Pero debido al número cada vez mayor de núcleos por CPU, parece que la industria se ha interesado más recientemente en la concurrencia de paso de mensajes, como se puede ver en el aumento de las bibliotecas de paso de mensajes (por ejemplo, Akka para JVM) o lenguajes de programación (por ejemplo, Erlang ).

La biblioteca Akka mencionada anteriormente, que está respaldada por un modelo de actor teórico simplifica la creación de aplicaciones concurrentes, ya que no tiene que lidiar más con bloqueos, monitores o transacciones. Por otro lado, requiere un enfoque diferente para diseñar la solución, es decir, pensar de manera jerárquicamente compuesta de actores. Se podría decir que requiere una mentalidad totalmente diferente, que a su vez puede ser más difícil que usar la concurrencia compartida de estado simple.

La programación concurrente es difícil debido al no determinismo observable, pero cuando se usa el enfoque correcto para el problema dado y la biblioteca correcta que apoya ese enfoque, se pueden evitar muchos problemas.

    
respondido por el Jernej Jerin 23.08.2015 - 11:38
fuente
0

La primera vez que me enseñaron fue que podía plantear problemas al ver un programa simple que comenzó con 2 subprocesos e hizo que ambos se imprimieran en la consola al mismo tiempo del 1-100. En lugar de:

1
1
2
2
3
3
...

Obtienes algo más como esto:

1
2
1
3
2
3
...

Vuelve a ejecutarlo y puedes obtener resultados totalmente diferentes.

La mayoría de nosotros hemos sido capacitados para asumir que nuestro código se ejecutará de forma secuencial. Con la mayoría de los subprocesos múltiples, no podemos dar esto por sentado "fuera de la caja".

    
respondido por el Morgan Herlocker 02.06.2011 - 22:15
fuente
-3

Intente usar varios martillos para hacer puré en un montón de clavos muy próximos a la vez, sin comunicación entre los que sostienen los martillos ... (suponga que tienen los ojos vendados).

Escala esto a la construcción de una casa.

Ahora intenta dormir por la noche imaginando que eres el arquitecto. :)

    
respondido por el Macke 02.06.2011 - 21:10
fuente
-3

Parte fácil: use el multihilo con características contemporáneas de marcos, sistemas operativos y hardware, como semáforos, colas, contadores entrelazados, tipos de caja atómica, etc.

Parte difícil: implemente las características en sí mismas, sin usar las características en primer lugar, puede ser, salvo algunas capacidades muy limitadas de hardware, confiando solo en la coherencia de temporización entre varios núcleos.

    
respondido por el user7071 02.06.2011 - 21:16
fuente

Lea otras preguntas en las etiquetas