How to Harness Node.js Worker Threads with Shared Memory and CGroup Limits
This article demonstrates creating Node.js Worker Threads to compute Fibonacci numbers, explains message passing via workerData and parentPort, explores shared memory with SharedArrayBuffer, and shows how to isolate and limit thread CPU usage using Linux CGroup controls, including retrieving thread IDs via a custom native addon.
Creating a Worker Thread
Node.js uses an event‑loop model; long‑running calculations block the loop and reduce throughput. Worker Threads can offload occasional CPU‑intensive work such as computing a Fibonacci sequence.
const { Worker } = require('worker_threads');
const worker = new Worker('./thread.js', {
workerData: { number: 10 }
});
worker.on('message', result => {
console.log(result);
});In thread.js the worker receives workerData, computes the Fibonacci number and sends the result back via parentPort.
const { workerData, parentPort } = require('worker_threads');
const { number } = workerData;
parentPort.postMessage(fib(number));
function fib(n) {
return n < 1 ? 0 :
n <= 2 ? 1 :
fib(n-1) + fib(n-2);
}Key points
Initial data can be passed to a worker through workerData. parentPort provides a MessageChannel for communication between parent and worker.
MessageChannel details
The messages are transferred using the HTML structured clone algorithm, which unlike JSON can handle circular references, built‑in types such as RegExp, BigInt, Map, Set, and binary buffers like ArrayBuffer or SharedArrayBuffer.
Shared memory
Primitive values are copied, but a SharedArrayBuffer allows true shared memory between threads.
const { Worker } = require('worker_threads');
const worker = new Worker('./thread.js', {
workerData: { number: 10 }
});
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
const msg = { buffer: sharedUint8Array };
worker.postMessage(msg);
worker.on('message', result => {
console.log('at main', result, msg);
});In the worker the buffer is filled with a value and the result is posted back.
const { workerData, parentPort } = require('worker_threads');
const { buffer } = workerData;
buffer.fill(5, 0, 4);
console.log('at worker', { buffer });
parentPort.postMessage('ok');Running the program shows both the main thread and the worker see the same filled buffer.
Isolation and CPU limitation
When deploying on serverless/FaaS platforms, limiting a worker’s CPU can be useful. The example shows how to obtain the Linux thread ID (TID) via a custom native addon that calls syscall(SYS_gettid), then place that TID into a CGroup.
#include <node.h>
#include <v8.h>
#include <unistd.h>
#include <sys/syscall.h>
pid_t gettid(void) { return syscall(SYS_gettid); }
// ...export gettid as a Node.js function...After building the addon, the worker prints its PID, TID and WorkerThreadId.
PID: 9041, TID: 9048, WorkerThreadId: 1
PID: 9041, TID: 9049, WorkerThreadId: 2Using cgcreate and cgset a CGroup named wt is created with a 25 % CPU quota, and the TIDs are added to the group via echo TID > /sys/fs/cgroup/cpu/wt/tasks. The top -H output confirms the CPU usage drops to the configured limits.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Node Underground
No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
