Multi Threading in Node.js

Sathish
4 min readJun 6, 2023

Whatt!! Is it really true? All these years I learnt - node.js is single threaded!! No, this is wrong. You are referring to child processes here!!

I also had the same feeling when I learnt about it.

Node.js web server is single threaded. Here also, when there are blocking I/O operations, Event loop delegates it to the Thread pool, means offloading operations to the system Kernel (kernel supports multi threading).

Worker threads were introduced in node.js as an experimental feature in Node V10. It became stable in version 12. Unlike child_process or cluster, worker_threads can share memory. They do so by transferring ArrayBuffer instances or sharing SharedArrayBuffer instances.

How worker thread works?

Node.js spins a separate child process for main thread. But all worker threads runs within that process. Note — No separate process is spinned for each worker thread.

I am not going into the details of how worker threads work, as there are many blogs and Monster is always there — https://chat.openai.com/ (prompt for how node.js worker threads work).

Here are the main components of worker_threads.

  • Worker — Use to spawn a worker thread. Worker class also supports some events as well — exit (finished), error, message(to read message from worker thread), messageerror, and online (triggered as soon as worker started).
  • parentPort — Use to communicate with parent (main thread), postMessge() is used to send data from worker thread to main thread.
  • workerDataUsed by workthreads to read data passed by main thread.

Please refer this to know about more offerings from worker_threads.

Enough of theories, let’s get into action!!

I have used worker threads to find all prime numbers between 1 to 10millions.

Save the below code in thread.js

const { Worker } = require('worker_threads');
const min = 2;
const max = 1e7;
const threadCount = 2;
const threads = new Set();
let primes = [];

function createWorker (workerData) {
const worker = new Worker('./worker.js', { workerData })
worker.on('error', (err) => { throw err })
worker.on('message', (msg) => {
primes = primes.concat(msg)
});
worker.on('exit', () => {
threads.delete(worker);
if (threads.size === 0) {
console.log("Prime Numbers", primes.join(","));
}
});
return worker
}

const range = Math.ceil((max - min) / threadCount);
let start = min;
for(let i = 0; i <threadCount; i++) {
threads.add(createWorker({start, range}));
start = start + range + 1;
}
  • Here, worker_threads module is required for multi-threading and 2 threads are used. Worker class accepts file path and json options. In json options, data is passed to worker threads.
  • Finding prime numbers between 1–5million is assigned to Thread1, and 5000001–10million is assigned to thread2. As part of workerData variable, start and range is passed.
  • worker is registered with different events —
  • error — this will be triggered when worker throws an error.
  • message — this will be triggered when worker sends a message to main thread.
  • exit — this will be triggered when worker finished its task.

Let’s get into the work thread. Save the below code in worker.js

const { parentPort, workerData } = require('worker_threads');
let primes = [];
function generatePrimes(start, range) {
let isPrime = true;
let end = start + range;
for (let i = start; i < end; i++) {
for (let j = start; j < Math.sqrt(end); j++)
if (i !== j && i % j === 0) {
isPrime = false;
break;
}
}
if (isPrime) {
primes.push(i);
}
isPrime = true;
}
parentPort.postMessage(primes);
}
generatePrimes(workerData.start, workerData.range);
  • parentPort.postMessage() — used send data to main thread.
  • workerData — used to read incoming workerData.

We are done!! run node thread.js to experience the multithreading.

I did some experiments to ensure multithreading is really working.I set the threadCount to 1 and ran it. It took almost 10seconds. When I ran again with threadCount=2, it took 5seconds and with threadCount=5, it took 2seconds to complete. I also verified no extra child processes were spawn during execution. So, yeaaahh!! Node.js offers multi threading.

Here are my learnings:

  • Don’t use worker_threads for I/O intensive operations, Node.js takes care of it using typical non-blocking I/O mechanism.
  • Currently Worker class constructor accepts only file path. It will be better if it accepts function names and passing callback functions.
  • Node.js made a big shift in supporting this worker_threads. This definitely a treat for developers. Our typical answer “when not to use node.js” — f̵o̵r̵ ̵C̵P̵U̵ ̵i̵n̵t̵e̵n̵s̵i̵v̵e̵ ̵o̵p̵e̵r̵a̵t̵i̵o̵n̵s̵ . This statement is no more valid. Now, it supports CPU intensive stuffs.
  • For sure, it’s gonna hard to convince people that node.js supports real multi-threading (not just by using child_process).

--

--

Sathish

Software Architect ★ Developer ★ Troubleshooter