GPUs and multi-core CPUs are making computers more powerful. Additionally, as threads are used to maximize application efficiency and responsiveness, applications are becoming more complicated.
This article will define multithreading in nodejs and describe how Node.js's worker pools and the event loop in Node.js interact to manage asynchronous activities. We'll also explore how to create and manage threads using the Node.js worker-threads module.
History of Single Threaded JavaScript
At its core, Javascript is a single-threaded, synchronous, blocking language. It was first designed to function within web browsers, enabling animations, form validations, and web page interactions.
However, some browser operations could take longer to complete. Single-threaded tasks can stall the synchronous execution flow and make UI interactions unresponsive. JavaScript can be asynchronous, allowing us to conduct certain time-consuming processes in parallel without creating or synchronizing threads.
Nodejs Single Threaded
Our Node.js applications are single-threaded. Although we can execute processes simultaneously, we don't make or sync threads. When transferring data back to our JavaScript code, the JavaScript runs in a single thread because the virtual machine and operating system handle the I/O in parallel.
In other words, everything but our JavaScript code operates in parallel. All we do is asynchronous I/O, and this is fantastic. Our JavaScript code is made up of brief synchronous blocks that execute quickly and transmit data to files and streams; as a result, it does not obstruct the execution of other JavaScript code.
Will it be Useful to Use the Multithreading Paradigm in I/O-bound Tasks?
However, having threads idle while waiting for an I/O task to finish is not very resource-efficient for network applications because threads consume resources whether they are active or idle. Each thread uses a piece of the CPU, and when threads are waiting to complete I/O activities, they waste CPU time that threads could use with actual CPU work.
The CPU must save the current thread's local data, application pointer, etc., and load the next thread's local data, application pointer, etc. of the next thread to run when it switches from executing one thread to another, adding overhead to the performance of the entire application. Additionally, threads can access shared data; this can cause various concurrency problems, including deadlocks, resource hunger, and race conditions.
By eliminating waiting threads, event-driven asynchronous I/O lowers the total number of concurrent threads. This improves the scalability of the application and results in a simpler application design.
How Nodejs Uses Threads at The Backend
The two thread types in Node.js are:
Only one thread of the Event Loop (aka the main thread).
Threads in the worker pool, also known as thread pool.
The Event Loop (initialization and callbacks) in Node.js executes JavaScript code and is in charge of handling non-blocking asynchronous requests like network I/O. Further in this article, multithreading in nodejs, we will look into the need for threads to perform CPU-intensive tasks.
Need for Threads to Perform CPU Intensive Tasks
We must perform synchronously demanding tasks, such as intricate calculations on a huge dataset. A lengthy synchronous block of code will potentially block the remaining code. Take a 10-second calculation, for example. If we run a web server, all other requests get blocked for at least 10s because of that calculation. That is a catastrophe; anything longer than 100 ms might be excessive.
JavaScript and Node.js were not supposed to be used for CPU-bound tasks. JavaScript's single-threaded nature will cause the browser's UI to become unresponsive and Node.js to queue any I/O events.
Why Will we Never Have Multithreading in JavaScript?
Many could conclude that the best course at this point would be to introduce a new module enabling us to generate and sync threads into the Node.js core. However, it is not feasible.
JavaScript would become a different language if threads were included. Since multithreading in nodejs will likely require changing the language, we cannot simply add threads as a new set of classes or functions. For thread cooperation, languages that now support it contain keywords like synchronized.
Unfortunately, a sophisticated server-side platform like Node.js doesn't offer an amicable solution to this use case. Some numeric types in Java, for instance, are not atomic; if their access is not synchronized, it may result in two threads changing a variable's value. The variable would have a few bytes modified by one thread and a few bytes changed by the other thread after both threads had accessed it, and it would not produce a valid value as a result. Let’s see more about multithreading in nodejs.
Worker Threads
Isolated contexts exist for worker threads. They communicate with the main process through message passing. Therefore race conditions problems that normal threads face are avoided! However, they consume much less memory because they share the same process.
You can send objects designed for memory sharing, such as ArrayBuffer or SharedArrayBuffer, to Worker threads. Use them only when dealing with big amounts of data and CPU-intensive operations.
How to Use Worker Threads in Node.js
If you are running Node.js 10.5.0, you can immediately start using the worker threads module. However, if you're running any version below 11.7.0, you must enable it by calling Node.js with the —experimental-worker flag.
Instead of developing your pool implementation, you should probably search for a general or specific one in npm. For effective async tracking of a worker pool, Node.js offers AsyncResource.
Let's look at an easy example. We'll first implement the main file, make a Worker thread, and supply it with information. The API is event-driven, but I'm going to encapsulate it in a promise that fulfils in response to the first message the Worker sends:
const { Worker } = require('worker_threads')
function runService(workerData) {
return new Promise((resolve, reject) => {
const worker = new Worker('./service.js', { workerData });
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0)
reject(new Error(`Worker stopped with exit code ${code}`));
})
})
}
async function run() {
const result = await runService('world')
console.log(result);
}
run().catch(err => console.error(err))
When we're done, we watch for a message from the Worker thread by listening to the message event. Execute the service:
const { workerData, parentPort } = require('worker_threads')
/* You can do any heavy stuff here in a synchronous way without blocking the "main thread.*/
parentPort.postMessage({ hello: workerData })
Here, we require the workerData that the main app supplied and a means of sending data back to the main app. To accomplish this, we use the parentPort, which includes a postMessage method to give the outcome of our processing.
To run the example, remember to use the --experimental-worker flag if you are using any version before Node.js 11.7:
The libuv library, which handles I/O operations like reading files from a disc or making network requests, provides hidden threads through Node. js, which is multithreaded on its own.
What number of threads can node js support?
Sure, Node. Four more threads are created by js in addition to the main thread, but none of them are used for network I/O, such as database activities.
Why is NodeJS referred to as single-threaded?
The Multi-Threaded Request/Response Stateless Model is not adhered to by the Node JS Platform. The Single-Threaded with Event Loop Model is used. The main source of inspiration for the Node JS Processing paradigm was JavaScript Event-based model with a JavaScript callback mechanism.
What makes NodeJS single-threaded?
Node JS Platform only supports a single thread, so it doesn't adhere to the multi-threaded request/response stateless model. It uses an event loop and is single-threaded. The JavaScript event-based paradigm with JavaScript callback mechanism was the main inspiration for the Node JS processing model.
Is the Nodejs thread safe?
All are safe for the thread. Because JavaScript is single-threaded and lacks threads, it cannot execute two javascript statements simultaneously.
Conclusion
In conclusion, multithreading in Node.js offers several advantages such as improved performance, better resource utilization, and enhanced concurrency. It enables developers to handle multiple tasks simultaneously, enhancing the scalability of applications. However, implementing multithreading in Node.js can be complex and requires careful synchronization to avoid race conditions and ensure data integrity. Overall, multithreading can significantly benefit certain types of Node.js applications but requires careful consideration and implementation.