0% found this document useful (0 votes)
4 views

Duplexing - Concurrency in Node JS

Node.js operates on a single-threaded model but achieves concurrency through an event loop and non-blocking I/O, allowing it to handle multiple tasks simultaneously. It utilizes Libuv for low-level tasks and a thread pool for heavy I/O operations, while worker threads enable parallel execution for CPU-bound tasks. The cluster module enhances scalability by creating multiple worker processes that share the same server port, distributing the workload across CPU cores.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

Duplexing - Concurrency in Node JS

Node.js operates on a single-threaded model but achieves concurrency through an event loop and non-blocking I/O, allowing it to handle multiple tasks simultaneously. It utilizes Libuv for low-level tasks and a thread pool for heavy I/O operations, while worker threads enable parallel execution for CPU-bound tasks. The cluster module enhances scalability by creating multiple worker processes that share the same server port, distributing the workload across CPU cores.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

Node.

js Concurrency and Multi-Threading

Understanding Node.js Threading Model

Node.js is often described as single-threaded because it has a single


JavaScript execution thread. This means only one piece of JavaScript code
can run at a time within the main thread. However, Node.js excels at
handling multiple tasks concurrently (simultaneously) despite this single
thread. This is achieved through a combination of techniques:

1. Event Loop and Non-Blocking I/O:

Imagine the event loop as the brain of Node.js. It's a continuous loop that
manages events and tasks. Here's how it works:

● When your Node.js application starts, the event loop begins.


● Your JavaScript code runs in the main thread, controlled by the event
loop.
● When your code encounters an I/O operation (like reading a file,
making a network request), the event loop doesn't wait for it to finish.
● Instead, it hands off the I/O task to the operating system and
continues executing other code.
● Once the I/O operation is completed, the operating system notifies
the event loop.
● The event loop then adds a callback function (a piece of code to be
executed) associated with that I/O operation to a queue.
● When the event loop finishes its current task, it checks the queue and
executes any pending callback functions.

Benefits of Non-Blocking I/O:

● Efficiency: The event loop doesn't get blocked waiting for I/O
operations, allowing your application to handle more concurrent
tasks.
● Responsiveness: Your application remains responsive while I/O
operations are happening in the background.
2. Libuv and Thread Pool (Behind the Scenes):

Libuv is a C library embedded within Node.js that handles low-level tasks


like file system operations and network communication. It provides an
event loop and a thread pool:

● Event Loop: Similar to the main event loop, libuv's event loop
manages its own set of events and tasks related to I/O operations.
● Thread Pool: For certain I/O operations that require more processing
power, libuv can utilize a thread pool. This pool consists of multiple
threads that can run concurrently to perform these tasks.

Think of it this way:

● The main event loop manages your JavaScript code and I/O
operations that don't require heavy processing.
● Libuv's event loop and thread pool handle more demanding I/O tasks
efficiently, freeing up the main thread for your JavaScript code.

3. Worker Threads (For CPU-Bound Tasks):

While Node.js excels at I/O concurrency, it can't run multiple JavaScript


code chunks truly in parallel within the main thread. This is where worker
threads come in, introduced in Node.js 10.5.0.

● Worker threads are separate JavaScript execution environments that


run parallel to the main thread.
● They are ideal for CPU-bound tasks (like complex calculations) that
would block the main thread if run directly.
● Each worker thread has its event loop and can communicate with the
main thread using message passing.

Example: Imagine you're resizing images in your Node.js application.


Resizing is CPU-bound. By using a worker thread, you can offload the
image resizing task, allowing the main thread to continue handling other
requests while the resizing happens in the background.
4. Cluster Module (Scaling Across Cores):

The cluster module is another tool for concurrency in Node.js. It allows you
to create multiple worker processes that share the same server port.

● The main process (master) forks (creates) worker processes based


on the number of CPU cores available.
● Each worker process runs a separate instance of your Node.js
application.
● This allows you to distribute your application's workload across
multiple cores, improving performance and scalability.

Think of it this way:

● Imagine a restaurant with a single chef (main thread). Processing


orders takes time, leading to a queue.
● By hiring more chefs (worker processes), the restaurant can handle
multiple orders concurrently, reducing waiting times.

Here are some code examples that illustrate the concepts discussed:

1. Event Loop and Non-Blocking I/O:

const fs = require("fs");

console.log("Starting operation...");

fs.readFile("data.txt", (err, data) => {


if (err) {
console.error("Error reading file:", err);
} else {
console.log("File data:", data.toString());
}
});
console.log("Doing other stuff while waiting for file
read...");

In this example:

● We schedule a file read operation using fs.readFile.


● The operation is non-blocking, meaning the code continues to
console.log('Doing other stuff...').
● Once the file is read, the callback function is added to the event loop
queue.
● When the event loop finishes its current task, it executes the callback,
printing the file data.

2. Worker Threads (For CPU-Bound Tasks):

const { Worker } = require('worker_threads');

const worker = new Worker('./worker.js', { workerData:


{ number: 1000000 } });

worker.on('message', (result) => {


console.log('Fibonacci result:', result);
});

worker.on('error', (err) => {


console.error('Worker error:', err);
});

console.log('Main thread is still responsive...');


worker.js

const { parentPort } = require('worker_threads');

function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}

const { number } = parentPort;


const result = fibonacci(number);
parentPort.postMessage(result);

In this example:

● The main thread creates a worker and sends data to it (the number to
calculate the Fibonacci sequence).
● The worker calculates the Fibonacci number (CPU-bound task) in a
separate thread.
● The worker sends the result back to the main thread using message
passing.

3. Cluster Module (Scaling Across Cores):

app.js (Main Application)

const cluster = require('cluster');


const http = require('http');

if (cluster.isMaster) {
const numCPUs = require('os').cpus().length;
console.log(`Master process ${process.pid} is
running`);

for (let i = 0; i < numCPUs; i++) {


cluster.fork();
}

cluster.on('exit', (worker, code, signal) => {


console.log(`Worker ${worker.process.pid} died`);
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello, world! ');
}).listen(8000);

console.log(`Worker ${process.pid} started`);


}

Explanation:

● The master process creates worker processes based on the number


of CPU cores.
● Each worker process runs an HTTP server, allowing the application to
handle more concurrent connections.

You might also like