O NodeJs é de thread único?
O Node.js executa o código JavaScript em um único thread, o que significa que seu código pode executar apenas uma tarefa por vez. Mas o próprio Node.js é multiencadeado e fornece encadeamentos ocultos por meio da biblioteca `libuv`, que lida com operações de E/S, como ler arquivos de um disco ou solicitações de rede. Por meio do uso de encadeamentos ocultos, o Node.js fornece métodos assíncronos que permitem que seu código faça solicitações de E/S sem bloquear o encadeamento principal.
O Node.js apresentou o módulo worker-threads para nós. Esse módulo permite criar threads e executar tarefas Javascript em paralelo. Se um thread terminar uma tarefa, ele enviará uma mensagem para o thread principal que contém o resultado para que possamos usá-lo no thread principal. Os threads de trabalho não bloqueiam o thread principal, portanto, podemos dividir e distribuir nossa tarefa para vários pars para otimizá-la.
Vamos fazer um exemplo disso e olhá-lo mais de perto.
Estamos criando um projeto Nodejs e instalando o framework Express
mkdir multiple-thread
cd multiple-thread
npm init -y
npm install express
Em primeiro lugar, existem processos e threads. Devemos saber as diferenças entre eles.
O que é Processo?
Um processo está executando um programa no sistema operacional. Ele pode executar uma tarefa por vez.
Vamos olhar mais de perto o processo mais de perto. Crie um arquivo JS e registre o processo.
//ex-process.js
const processName = process.argv.slice(2)[0]
let count = 0;
while(true){
count++;
if(count === 3333 || count=== 6666){
console.log(`${processName}: ${count}`)
}
}
node ex-process.js FirstEx &
// & symbol means the Node program to run in the background, so we can use
our teminal for other works
Output
[1] 82942
FirstEx: 3333
FirstEx: 6666
ps |grep node
Output
82942 ttys001 2:38.85 node ex-process.js FirstEx
83105 ttys001 0:00.00 grep node
Threads são uma maneira relativamente leve de implementar vários caminhos de execução dentro de um aplicativo. No nível do sistema, os programas são executados lado a lado, com o sistema distribuindo o tempo de execução para cada programa com base em suas necessidades e nas necessidades de outros programas.
Vamos codificar o Express Server com a rota sem bloqueio e com as rotas de bloqueio
Vamos codificar uma rota de bloqueio normal para os usuários. Em app.js;
const express = require('express');
const app = express();
app.get('/hello', (req,res)=>{
res.status(200).send(`Hello`);
})
app.get('/blocking-route', (req,res)=>{
console.log('Blocking route called')
let counter = 0;
for (let i = 0; i < 15_000_000_000; i++) {
counter++;
}
res.status(200).send(`result is ${counter}`);
})
app.listen(7333, ()=> console.log('Server started on port 7333'));
Ele não responde até blocking-routeterminar e enviar uma resposta.
Por que não nos respondeu instantaneamente?
Neste exemplo, o loop for vinculado à CPU bloqueia o thread principal. Enquanto o thread principal está bloqueado, o Node.js não pode processar nenhuma consulta até que o processo vinculado à CPU tenha terminado. portanto, nenhuma das outras rotas /hellofunciona como a /blocking-routerota é carregada.
Assim, se tivermos uma API que tenha milhares de GETrequisições simultâneas para a rota não bloqueante como /hello, apenas uma requisição para o /blocking-routeirá bloquear todas as rotas na API. Isso significa que todas as rotas de aplicativos não respondem.
DAAA IT's javascript, não podemos usar outro thread
Dudeeee Não é só JS. É o Nodejs. Podemos descarregar uma tarefa vinculada à CPU com threads de trabalho.
Antes de tudo, devemos verificar nossos núcleos com nproco comando
nproc
output
8
Vamos criar outro arquivo JS como worker.js, o nome fica a seu critério. Neste arquivo, fazemos tarefas intensivas de CPU
//worker.js
const { parentPort } = require("worker_threads");
let counter = 0;
console.log("Worker started");
for (let i = 0; i < 15_000_000_000; i++) {
counter++;
}
parentPort.postMessage(counter);
//app.js
const express = require("express");
const { Worker } = require("worker_threads");
const app = express();
app.get("/blocking-route", async (req, res) => {
console.log("Blocking route called");
let counter = 0;
for (let i = 0; i < 15_000_000_000; i++) {
counter++;
}
res.status(200).send(`result is ${counter}`);
});
app.get("/hello", (req, res) => {
res.status(200).send(`Hello`);
});
app.get("/non-blocking-route", async (req, res) => {
console.log("Non-blocking route called");
const worker = new Worker("./worker.js");
worker.on("message", (data) => {
res.status(200).send(`result is ${data}`);
});
worker.on("error", (err) => {
res.status(404).send(` error ${err}`);
});
console.log("I am not blocked");
});
app.listen(7333, () => console.log("Server started on port 7333"));
Vamos iniciar o servidor com node app.js. Abra seu navegador e vá http://localhost:7333/non-blocking-routeantes que ele retorne uma resposta, vá para http://localhost:7333/hello. Você percebeu isso? Recebemos uma resposta instantaneamente. Não há bloqueio. Não esperamos que as outras rotas terminassem de carregar. Porque a tarefa vinculada à CPU é descarregada e está trabalhando em outro thread, e o thread principal lida com todas as solicitações. Podemos usar mais trabalhadores para melhorar nosso desempenho. Por exemplo, tenho 8 núcleos e posso dividir meu Processo em 8.
Conclusão
Trabalhadores (threads) podem ajudar com operações JavaScript com uso intensivo de CPU. Ao contrário child_processde ou cluster, worker_threadspode compartilhar memória.
Neste artigo, tentei facilitar e explicar como funcionam os worker_threads?
Obrigado por ler





































![O que é uma lista vinculada, afinal? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)