
O Node.js agora se tornou um membro da caixa de ferramentas para a construção de serviços de aplicativos de rede de alta simultaneidade. Por que o Node.js se tornou o queridinho do público? Este artigo começará com os conceitos básicos de processos, threads, corrotinas e modelos de E/S e fornecerá uma introdução abrangente ao Node.js e ao modelo de simultaneidade.
Geralmente chamamos uma instância em execução de um programa de processo. É uma unidade básica para alocação e agendamento de recursos pelo sistema operacional. Geralmente inclui as seguintes partes:
进程表. Cada processo ocupa uma进程表项(também chamada进程控制块). Essa entrada contém status importantes do processo, como contador de programa, ponteiro de pilha, alocação de memória, status de arquivos abertos e informações de agendamento. . informações para garantir que, após a suspensão do processo, o sistema operacional possa reativá-lo corretamente.O processo possui as seguintes características:
Deve-se notar que se um programa for executado duas vezes, mesmo que o sistema operacional possa permitir o compartilhamento de código (ou seja, apenas uma cópia do código está na memória), isso não pode alterar o fato de as duas instâncias do programa em execução serem duas instâncias diferentes. processa fato.
Durante a execução do processo, por diversos motivos como interrupções e escalonamento da CPU, o processo irá alternar entre os seguintes estados:

No diagrama de mudança de estado do processo acima, podemos ver que um processo pode mudar do estado de execução para o estado pronto e bloqueado, mas apenas o estado pronto pode ser alternado diretamente para o estado de execução. Isso ocorre porque:
Às vezes, precisamos usar threads para resolver os seguintes problemas:
Em relação aos threads, precisamos saber os seguintes pontos:
Agora que entendemos as características básicas dos threads, vamos falar sobre vários tipos de threads comuns.
Threads de estado do kernel são threads suportados diretamente pelo sistema operacional. Seus principais recursos são os seguintes:
Threads de modo de usuário são threads completamente construídos no espaço do usuário. Suas principais características são as seguintes:
Um processo leve (LWP) é um thread de usuário construído e suportado pelo kernel. Seus principais recursos são os seguintes:
O espaço do usuário só pode usar threads do kernel por meio de processos leves (LWP). ponte entre threads de modo de usuário e threads de kernel, portanto, apenas suportando threads de kernel pode haver um processo leve (LWP).
A maioria das operações de processos leves (LWP) requer espaço de modo de usuário para iniciar a chamada do sistema. é relativamente caro (requer alternar entre o modo de usuário e o modo kernel);
cada processo leve (LWP) precisa ser associado a um thread de kernel específico, portanto:
eles podem acessar seus próprios processos Todos os espaços de endereço compartilhados e recursos do sistema);
Acima, apresentamos brevemente os tipos de threads comuns (threads de estado do kernel, threads de estado do usuário, processos leves). No uso real, você pode usá-los livremente de acordo com suas próprias necessidades. combinação, como modelos comuns um-para-um, muitos-para-um, muitos-para-muitos e outros. Devido às limitações de espaço, este artigo não apresentará muito sobre isso.
, também chamada de Fiber, é um mecanismo de execução de programa construído em threads que permite aos desenvolvedores gerenciar por conta própria o agendamento de execução, manutenção de estado e outros comportamentos. Suas principais características são
Em JavaScript, async/await que usamos com frequência é uma implementação de corrotina, como no exemplo a seguir:
function updateUserName(id, name) {
const usuário = getUserById(id);
usuário.updateName(nome);
retornar verdadeiro;
}
função assíncrona updateUserNameAsync(id, nome) {
const usuário = aguarda getUserById(id);
aguarde user.updateName(nome);
retornar verdadeiro;
} No exemplo acima, a sequência de execução lógica dentro das funções updateUserName e updateUserNameAsync é:
truegetUserById atribuir seu valor de retorno à variável user ;updateName de user ;A principal diferença entre os dois está no controle de estado durante a operação real:
updateUserName , ele é executado em sequência de acordo com a sequência lógica mencionada acima,updateUserNameAsync , também é executado em sequência de acordo com; a sequência lógica mencionada acima, mas ao encontrar await , updateUserNameAsync será suspenso e salvará o estado atual do programa no local suspenso. Ele não ativará updateUserNameAsync novamente até que o fragmento do programa após await retorne e restaure o estado do programa antes da suspensão. e depois continue para o próximo programa.A partir da análise acima, podemos adivinhar com ousadia: o que as corrotinas precisam resolver não são os problemas de simultaneidade do programa que os processos e threads precisam resolver, mas os problemas encontrados ao processar tarefas assíncronas (como operações de arquivo, solicitações de rede, etc.); em Antes de async/await , só podíamos lidar com tarefas assíncronas por meio de funções de retorno de chamada, o que poderia facilmente nos fazer cair no回调地狱e produzir uma confusão de código que geralmente é difícil de manter. Por meio de corrotinas, podemos alcançar a sincronização do código assíncrono. .
O que precisa ser mantido em mente é que a principal capacidade das corrotinas é ser capaz de suspender um determinado programa e manter o estado da posição de suspensão do programa, e retomá-lo na posição suspensa em algum momento no futuro, e continuar a execute o próximo segmento após o programa de posição de suspensão.
Uma operação I/O completa precisa passar pelos seguintes estágios:
I/O para o kernel por meio de uma chamada do sistema,I/O (dividida); no estágio de preparação e na fase de execução real) e retorna os resultados do processamento ao thread do usuário.Podemos dividir aproximadamente as operações I/O em quatro tipos:阻塞I/O ,非阻塞I/O ,同步I/O e异步I/O Antes de discutir esses tipos, primeiro nos familiarizamos com os dois conjuntos de operações a seguir. conceitos (aqui, suponha que o serviço A chame o serviço B):
阻塞/非阻塞:
阻塞调用非阻塞调用.同步/异步:
同步;回调após a conclusão da execução; . O resultado é notificado para A, então o serviço B é异步.Muitas pessoas costumam confundir阻塞/非阻塞com同步/异步, portanto, atenção especial precisa ser dada:
阻塞/非阻塞é para调用者do同步/异步被调用者Depois de entender阻塞/非阻塞e同步/异步, vamos examinar o I/O 模型específico.
: Depois que o thread do usuário inicia uma chamada do sistema I/O , o thread do usuário será阻塞imediatamente até que toda a operação I/O seja processada e o resultado seja retornado ao thread do usuário somente depois que o usuário entrar no thread do usuário. (thread) processo pode阻塞ser liberado e continuar a realizar operações subsequentes.
Características:
I/O , o processo do usuário (thread) não pode realizar outras operações,I/O pode bloquear o thread de entrada (thread), portanto, para responder à solicitação I/O a tempo, é necessário alocar um thread de entrada (thread) para cada solicitação, o que causará um enorme recurso uso e para solicitações de conexão longas, uma vez que os recursos de entrada (thread) não podem ser liberados por um longo tempo, se houver novas solicitações no futuro, ocorrerá um sério gargalo de desempenho.Definição
I/O em um thread (thread), se a operação I/O não estiver pronta, a chamada de I/O retornará um erro e o usuário não precisa entrar no thread (thread). Aguarde, mas use a pesquisa para detectar se a operação I/O está prontaI/O real bloqueará o thread do usuário até que o resultado da execução seja retornado ao tópico do usuário.Características:
I/O (geralmente usando um loop while ), o modelo precisa ocupar a CPU e consumir recursos da CPUI/O esteja pronta, o usuário; precisa entrar (o thread) thread não será bloqueado Quando a operação I/O estiver pronta, as operações I/O reais subsequentes impedirão que o usuário entre no thread (thread).Depois que o processo do usuário (thread) inicia uma chamada do sistema I/O , se a chamada I/O fizer com que o processo do usuário (thread) seja bloqueado, então a chamada I/O será同步I/O caso contrário, será异步I/O .
O critério para julgar se uma operação I/O同步ou异步é o mecanismo de comunicação entre os threads do usuário e as operações I/O . No
同步, a interação entre os threads do usuário e I/O é sincronizada por meio do buffer do kernel. isto é, o kernel sincronizará os resultados da execução da operação de I/O para o buffer e, em seguida, copiará os dados do buffer para o thread do usuário. Este processo bloqueará o thread do usuário até que a operação de I/O seja concluída异步I/O é sincronizada diretamente através do kernel, ou seja, o kernel copiará diretamente os resultados da execução da operação de I/O para o thread do usuário (thread). não Bloqueie o processo do usuário (thread).O Node.js usa um modelo de I/O assíncrona orientado a eventos e de thread único. Pessoalmente, acho que o motivo para escolher esse modelo é:
I/O . Como gerenciar recursos multithread de maneira razoável e eficiente e, ao mesmo tempo, garantir alta simultaneidade é mais complicado do que o gerenciamento de recursos de thread único.Resumindo, para fins de simplicidade e eficiência, o Node.js adota um modelo I/O assíncrona orientado a eventos e de thread único e implementa seu modelo por meio do EventLoop do thread principal e do thread auxiliar do Worker:
Deve-se observar que o Node.js não é adequado para executar tarefas com uso intensivo de CPU (ou seja, que exigem muitos cálculos); isso ocorre porque o EventLoop e o código JavaScript (código de tarefa de evento não assíncrono) são executados no mesmo thread (ou seja, o thread principal), e qualquer um deles Se for executado por muito tempo, poderá causar o bloqueio do thread principal. Se o aplicativo contiver um grande número de tarefas que requerem execução longa, isso reduzirá o rendimento do servidor e poderá até mesmo. fazer com que o servidor pare de responder.
Node.js é uma tecnologia que os desenvolvedores front-end terão que enfrentar agora e até mesmo no futuro. No entanto, a maioria dos desenvolvedores front-end tem apenas conhecimento superficial do Node.js para permitir que todos entendam melhor o modelo de simultaneidade do Node. .js, este artigo primeiro apresenta processos, threads e corrotinas, depois apresenta diferentes modelos I/O e, finalmente, fornece uma breve introdução ao modelo de simultaneidade do Node.js. Embora não haja muito espaço para introduzir o modelo de simultaneidade do Node.js, o autor acredita que ele nunca pode ser separado dos princípios básicos. Dominar os fundamentos relevantes e, em seguida, compreender profundamente o design e a implementação do Node.js obterá o dobro do resultado. com metade do esforço.
Finalmente, se houver algum erro neste artigo, espero que você possa corrigi-lo. Desejo a todos uma boa codificação todos os dias.