Na minha opinião, essa é uma pergunta de entrevista fabulosa - pelo menos supondo (1) que o candidato tenha profundo conhecimento de encadeamento e (2) o entrevistador também tenha profundo conhecimento e esteja usando a pergunta para investigar o candidato. É sempre possível que o entrevistador esteja procurando uma resposta específica e restrita, mas um entrevistador competente deve procurar o seguinte:
- Capacidade de diferenciar conceitos abstratos de implementação concreta. Eu jogo este aqui principalmente como um meta-comentário em alguns dos comentários. Não, não faz sentido processar uma única lista de palavras dessa maneira. No entanto, o conceito abstrato de um pipeline de operações, que pode abranger várias máquinas de diferentes capacidades, é importante.
- Na minha experiência (quase 30 anos de aplicativos distribuídos, com vários processos e com vários threads), distribuir o trabalho não é a parte mais difícil. Reunir os resultados e coordenar processos independentes é o local onde a maioria dos erros de segmentação ocorre (novamente, na minha experiência). Ao destilar o problema em uma cadeia simples, o entrevistador pode ver o quão bem o candidato pensa sobre coordenação. Além disso, o entrevistador tem a oportunidade de fazer todos os tipos de perguntas subsequentes, como "OK, e se cada tópico tiver que enviar sua palavra para outro tópico para reconstrução".
- O candidato pensa em como o modelo de memória do processador pode afetar a implementação? Se os resultados de uma operação nunca forem liberados do cache L1, isso é um bug, mesmo que não haja simultaneidade aparente.
- O candidato separa o encadeamento da lógica do aplicativo?
Este último ponto é, na minha opinião, o mais importante. Novamente, com base na minha experiência, torna-se exponencialmente mais difícil depurar código encadeado se o encadeamento for misturado com a lógica do aplicativo (basta ver todas as perguntas do Swing no SO para exemplos). Acredito que o melhor código multithread é escrito como um código single-thread independente, com transferências claramente definidas.
Com isso em mente, minha abordagem seria fornecer a cada thread duas filas: uma para entrada e outra para saída. O encadeamento bloqueia durante a leitura da fila de entrada, retira a primeira palavra da sequência e passa o restante da sequência para a fila de saída. Algumas das características dessa abordagem:
- O código do aplicativo é responsável por ler uma fila, fazer alguma coisa nos dados e gravar a fila. Não importa se é multiencadeado ou não, ou se a fila é uma fila na memória de uma máquina ou uma fila baseada em TCP entre máquinas que vivem em lados opostos do mundo.
- Como o código do aplicativo é escrito como um único encadeamento, é testável de maneira determinística, sem a necessidade de muitos andaimes.
- Durante sua fase de execução, o código do aplicativo possui a sequência que está sendo processada. Ele não precisa se preocupar com a sincronização com threads em execução simultânea.
Dito isto, ainda existem muitas áreas cinzentas que um entrevistador competente pode investigar:
- "OK, mas estamos olhando para conhecer seu conhecimento das primitivas de simultaneidade; você pode implementar uma fila de bloqueio?" Sua primeira resposta, é claro, deve ser que você usaria uma fila de bloqueio pré-criada da sua plataforma preferida. No entanto, se você entender os encadeamentos, poderá criar uma implementação de fila em menos de uma dúzia de linhas de código, usando as primitivas de sincronização suportadas pela plataforma.
- "E se uma etapa do processo demorar muito tempo?" Você deve pensar se deseja uma fila de saída limitada ou ilimitada, como poderá lidar com erros e efeitos na taxa de transferência geral se tiver um atraso.
- Como enfileirar com eficiência a cadeia de origem. Não é necessariamente um problema se você estiver lidando com filas na memória, mas pode ser um problema se estiver se movendo entre máquinas. Você também pode explorar wrappers somente leitura sobre uma matriz de bytes imutáveis subjacente.
Finalmente, se você possui experiência em programação simultânea, pode falar sobre algumas estruturas (por exemplo, Akka para Java / Scala) que já seguem esse modelo.