En mi opinión, esta es una pregunta fabulosa para la entrevista: al menos suponiendo que (1) se espera que el candidato tenga un conocimiento profundo de los hilos, y (2) el entrevistador también tiene un conocimiento profundo y está usando la pregunta para sondear al candidato. Siempre es posible que el entrevistador buscara una respuesta específica y estrecha, pero un entrevistador competente debería buscar lo siguiente:
- Capacidad para diferenciar conceptos abstractos de una implementación concreta. Lanzo este principalmente como un meta-comentario en algunos de los comentarios. No, no tiene sentido procesar una sola lista de palabras de esta manera. Sin embargo, el concepto abstracto de una tubería de operaciones, que puede abarcar varias máquinas de diferentes capacidades, es importante.
- En mi experiencia (casi 30 años de aplicaciones distribuidas, multiproceso y multiproceso), distribuir el trabajo no es la parte difícil. Recopilar los resultados y coordinar procesos independientes es donde ocurren la mayoría de los errores de subprocesamiento (de nuevo, en mi experiencia). Al resumir el problema en una cadena simple, el entrevistador puede ver qué tan bien piensa el candidato acerca de la coordinación. Además, el entrevistador tiene la oportunidad de hacer todo tipo de preguntas de seguimiento, como "OK, qué pasa si cada hilo tiene que enviar su palabra a otro hilo para la reconstrucción".
- ¿Piensa el candidato en cómo el modelo de memoria del procesador podría afectar la implementación? Si los resultados de una operación nunca se eliminan del caché L1, eso es un error, incluso si no hay concurrencia aparente.
- ¿El candidato separa el subproceso de la lógica de la aplicación?
Este último punto es, en mi opinión, el más importante. Nuevamente, según mi experiencia, se vuelve exponencialmente más difícil depurar el código enhebrado si el subproceso se mezcla con la lógica de la aplicación (solo mire todas las preguntas de Swing en SO para ver ejemplos). Creo que el mejor código de subprocesos múltiples está escrito como un código de subprocesos único autónomo, con transferencias claramente definidas.
Con esto en mente, mi enfoque sería dar a cada hilo dos colas: una para entrada y otra para salida. El hilo se bloquea mientras lee la cola de entrada, quita la primera palabra de la cadena y pasa el resto de la cadena a su cola de salida. Algunas de las características de este enfoque:
- El código de la aplicación es responsable de leer una cola, hacer algo a los datos y escribir la cola. No le importa si es multiproceso o no, o si la cola es una cola en memoria en una máquina o una cola basada en TCP entre máquinas que viven en lados opuestos del mundo.
- Debido a que el código de la aplicación está escrito como si fuera un solo subproceso, es comprobable de manera determinista sin la necesidad de muchos andamios.
- Durante su fase de ejecución, el código de la aplicación posee la cadena que se está procesando. No tiene que preocuparse por la sincronización con hilos que se ejecutan simultáneamente.
Dicho esto, todavía hay muchas áreas grises que un entrevistador competente puede investigar:
- "Está bien, pero estamos buscando ver su conocimiento de las primitivas de concurrencia; ¿puede implementar una cola de bloqueo?" Su primera respuesta, por supuesto, debería ser que usaría una cola de bloqueo preconstruida desde su plataforma de elección. Sin embargo, si comprende los hilos, puede crear una implementación de cola en menos de una docena de líneas de código, utilizando las primitivas de sincronización que su plataforma admita.
- "¿Qué pasa si un paso en el proceso lleva mucho tiempo?" Debe pensar si desea una cola de salida limitada o ilimitada, cómo podría manejar los errores y los efectos en el rendimiento general si tiene un retraso.
- Cómo poner en cola eficientemente la cadena de origen. No necesariamente es un problema si se trata de colas en memoria, pero podría ser un problema si se mueve entre máquinas. También puede explorar envoltorios de solo lectura sobre una matriz de bytes inmutable subyacente.
Finalmente, si tiene experiencia en programación concurrente, podría hablar sobre algunos marcos (por ejemplo, Akka para Java / Scala) que ya siguen este modelo.