Eu estive trabalhando em uma implementação de tempo de execução JavaScript multiencadeada na semana passada. Eu tenho uma prova de conceito feita em C ++ usando JavaScriptCore e boost.
A arquitetura é simples: quando o tempo de execução termina de avaliar o script principal, ele inicia e ingressa em um conjunto de encadeamentos, que começa a selecionar tarefas de uma fila de prioridade compartilhada, se duas tarefas tentam acessar uma variável simultaneamente, elas são marcadas como atômicas e elas disputam acesso .
O problema é que, quando mostro esse design a um programador JavaScript, recebo um feedback extremamente negativo e não faço ideia do porquê. Mesmo em particular, todos dizem que o JavaScript deve ser único, que as bibliotecas existentes teriam que ser reescritas e que os gremlins aparecerão e comerão todos os seres vivos se eu continuar trabalhando nisso.
Originalmente, eu também tinha uma implementação nativa de rotina (usando contextos de impulso), mas tive que abandoná-la (o JavaScriptCore é pedante sobre a pilha) e como não queria arriscar sua ira, decidi não mencioná-la.
O que você acha? O JavaScript é destinado a ser encadeado único e deve ser deixado em paz? Por que todos são contra a ideia de um tempo de execução JavaScript simultâneo?
Edit: O projeto está agora no GitHub , experimente você mesmo e deixe-me saber o que você pensa.
A seguir, é apresentada uma imagem das promessas em execução em todos os núcleos da CPU em paralelo, sem contenção:
fonte
Respostas:
1) A multithreading é extremamente difícil e, infelizmente, a maneira como você apresentou essa ideia até agora implica que você está subestimando seriamente a dificuldade.
No momento, parece que você está simplesmente "adicionando tópicos" ao idioma e se preocupando com como corrigi-lo e ter um desempenho mais tarde. Em particular:
Adicionar threads ao Javascript sem uma "solução para o problema de sincronização" seria como adicionar números inteiros ao Javascript sem uma "solução para o problema de adição". É tão fundamental para a natureza do problema que basicamente não faz sentido discutir se vale a pena adicionar multithreading sem uma solução específica em mente, não importa o quanto a desejemos.
Além disso, tornar todas as variáveis atômicas é o tipo de coisa que provavelmente fará com que um programa multithread tenha um desempenho pior do que seu equivalente de single-threaded, o que torna ainda mais importante testar o desempenho em programas mais realistas e ver se você está ganhando algo ou não.
Também não está claro para mim se você está tentando manter os threads ocultos do programador node.js.se planeja expô-los em algum momento, criando efetivamente um novo dialeto de Javascript para programação multithread. Ambas as opções são potencialmente interessantes, mas parece que você ainda nem decidiu qual delas está buscando.
Portanto, no momento, você está pedindo aos programadores que pensem em mudar de um ambiente single-threaded para um novo ambiente multithread que não tem solução para o problema de sincronização e nenhuma evidência que melhore o desempenho no mundo real, e aparentemente nenhum plano para resolver esses problemas.
É provavelmente por isso que as pessoas não o levam a sério.
2) A simplicidade e robustez do loop de evento único é uma enorme vantagem.
Os programadores Javascript sabem que a linguagem Javascript é "segura" contra as condições de corrida e outros bugs extremamente insidiosos que afetam toda a programação genuinamente multithread. O fato de que eles precisam de argumentos fortes para convencê-los a desistir de que a segurança não os deixa com a mente fechada, os torna responsáveis.
A menos que você possa, de alguma maneira, manter essa segurança, qualquer pessoa que queira mudar para um node.js multithread pode provavelmente mudar para um idioma como o Go, desenvolvido desde o início para aplicativos multithread.
3) Javascript já suporta "threads de segundo plano" (WebWorkers) e programação assíncrona sem expor diretamente o gerenciamento de threads ao programador.
Esses recursos já resolvem muitos casos de uso comuns que afetam os programadores Javascript no mundo real, sem abrir mão da segurança do loop de evento único.
Você tem algum caso de uso específico em mente que esses recursos não resolvem e que os programadores Javascript desejam uma solução? Nesse caso, seria uma boa ideia apresentar seu node.js multithread no contexto desse caso de uso específico.
PS O que me convenceu a tentar alternar para uma implementação node.js multithread?
Escreva um programa não trivial em Javascript / node.js que, em sua opinião, se beneficiaria de multithreading genuíno. Faça testes de desempenho neste programa de amostra no nó normal e no seu nó multithread. Mostre-me que sua versão melhora significativamente o desempenho em tempo de execução, a capacidade de resposta e o uso de múltiplos núcleos, sem introduzir bugs ou instabilidade.
Depois de fazer isso, acho que você verá pessoas muito mais interessadas nessa idéia.
fonte
Basta adivinhar aqui para demonstrar um problema em sua abordagem. Não posso testá-lo com a implementação real, pois não há link em nenhum lugar ...
Eu diria que é porque os invariantes nem sempre são expressos pelo valor de uma variável e 'uma variável' não é suficiente para ser o escopo de um bloqueio no caso geral. Por exemplo, imagine que temos um invariante
a+b = 0
(saldo de um banco com duas contas). As duas funções abaixo garantem que a invariante seja sempre mantida no final de cada função (a unidade de execução em JS de thread único).Agora, no seu mundo multithread, o que acontece quando dois threads são executados
withdraw
edeposit
ao mesmo tempo? Obrigado, Murphy ...(Você pode ter um código que trata + = e - = especialmente, mas isso não ajuda. Em algum momento, você terá um estado local em uma função e sem nenhuma maneira de 'bloquear' duas variáveis ao mesmo tempo que seu invariante irá ser violado.)
EDIT: Se o seu código é semanticamente equivalente ao código Go em https://gist.github.com/thriqon/f94c10a45b7e0bf656781b0f4a07292a , meu comentário é preciso ;-)
fonte
Há uma década, Brendan Eich (o inventor do JavaScript) escreveu um ensaio chamado Threads Suck , que é definitivamente um dos poucos documentos canônicos da mitologia do design do JavaScript.
Se está correto é outra questão, mas acho que teve uma grande influência sobre como a comunidade JavaScript pensa em concorrência.
fonte
O acesso atômico não se traduz em comportamento seguro de thread.
Um exemplo é quando uma estrutura de dados global precisa ser inválida durante uma atualização, como reformular um hashmap (ao adicionar uma propriedade a um objeto, por exemplo) ou classificar uma matriz global. Durante esse período, você não pode permitir que nenhum outro thread acesse a variável. Isso basicamente significa que você precisa detectar todos os ciclos de leitura, atualização e gravação e travar sobre isso. Se a atualização não for trivial, ela terminará em interromper o território problemático.
O Javascript foi encadeado e com sandbox único desde o início e todo o código é escrito com essas suposições em mente.
Isso tem uma grande vantagem em relação a contextos isolados e permite que dois contextos separados sejam executados em threads diferentes. Também quero dizer que as pessoas que escrevem javascript não precisam saber como lidar com as condições de corrida e vários outros pitfals multi-thread.
fonte
Sua abordagem vai melhorar significativamente o desempenho?
Duvidoso. Você realmente precisa provar isso.
Sua abordagem tornará mais fácil / rápido escrever código?
Definitivamente não, o código multithread é muitas vezes mais difícil de acertar do que o código thread único.
Sua abordagem será mais robusta?
Impasses, condições de corrida, etc., são um pesadelo para corrigir.
fonte
Sua implementação não consiste apenas em introduzir simultaneidade, mas em introduzir uma maneira específica de implementar simultaneidade, ou seja, simultaneidade por estado mutável compartilhado. Ao longo da história, as pessoas usaram esse tipo de simultaneidade e isso levou a muitos tipos de problemas. É claro que você pode criar programas simples que funcionam perfeitamente com o uso de simultaneidade de estado mutável compartilhada, mas o teste real de qualquer mecanismo não é o que ele pode fazer, mas pode ser dimensionado conforme o programa se torna complexo e mais e mais recursos são adicionados ao programa. Lembre-se de que o software não é algo estático que você constrói uma vez e acaba com ele, mas continua evoluindo ao longo do tempo e se algum mecanismo ou conceito puder '
Você pode observar outros modelos de concorrência (por exemplo, passagem de mensagens) para ajudá-lo a descobrir que tipo de benefícios esses modelos oferecem.
fonte
Isso é necessário. A falta de um mecanismo de simultaneidade de baixo nível no nó js limita suas aplicações em campos como matemática e bioinformática, etc ... Além disso, a simultaneidade com encadeamentos não necessariamente entra em conflito com o modelo de concordância padrão usado no nó. Existem semânticas conhecidas para threading em um ambiente com um loop de evento principal, como estruturas de interface do usuário (e nodejs), e elas são definitivamente excessivamente complexas para a maioria das situações em que ainda têm usos válidos.
Certamente, seu aplicativo Web médio não exigirá threads, mas tente fazer algo um pouco menos convencional, e a falta de uma primitiva de simultaneidade de baixo nível de som rapidamente o levará a algo que o ofereça.
fonte
Eu realmente acredito que é porque é uma ideia diferente e poderosa. Você está indo contra os sistemas de crenças. As coisas se tornam aceitas ou populares por meio da rede e não com base no mérito. Além disso, ninguém quer se adaptar a uma nova pilha. As pessoas automaticamente rejeitam coisas muito diferentes.
Se você conseguir criar uma maneira de transformá-lo em um módulo npm regular que parece improvável, talvez algumas pessoas o usem.
fonte