Going Multithread with Node.js
Node.js has been criticized for its design. Compared to programming languages like Java, C, or Python, node.js doesn’t have direct access to threads. How can we execute tasks concurrently?
Well, it was actually possible to use the Cluster module to execute code concurrently/in parallel long before Node.js 11, as described in the previous article.
But what if we’re on a server with only one core, what do we do?
In Node.js 11 we have the worker_Thread module, which allows us to create (spawn) multiple threads on a single core. In Node.js 10 we can actually use this module by adding the –experimental-worker flag, but in Node.js 11 we don’t need to use it anymore!
A simple use case
Let’s say we need to create a file with one million users, each with a first name, middle name, and last name.
I found this amazing Github project that provides an array of first, middle, and last names. We will use these JSON files in our project:
dominictarr/random-name
Let’s create a new project with the following file structure:
So let’s start with the main.js file:
As you can see, we used the FS-Extra package. It’s similar to FS, but all functions return a promise. It solves one of the big problems with this operation: memory usage. In fact, if we try to open too many files with Node.js, it will raise an exception and kill the main process because it can’t handle all of those open files at once (and is out of memory). In our for loop, await terminates the loop before the operation ends: in this way, we always open only one file per iteration.
Let’s take a look at the utils/index.js file:
In this case we’re just picking a random value out of an arbitrary array. This is useful when we need to get a random first, middle, or last name.
Running the code on my machine (2016 MacBook Pro, 2, 7ghz Intel Core I7, 16GB RAM) took 3 minutes and 32 seconds to complete. Let’s see how you can improve performance with node.js worker threads!
Using multiple threads
In order to use multithreading in this simple program, we need to make some changes to our code. Let’s start with the main.js file:
First, we need to import the Worker class from the Worker_Threads module. It allows us to create a new worker (thread) whenever we need it.
Next we can set the number of threads to be created: in this case, I decided to create only 10 threads.
Then we need to count the number of names generated per thread; This is easy, we just divide the total number of names by the number of threads.
For each thread, we need to create a new Worker. As you can see, this code will be placed in the worker.js file.
We’ll send the new Worker some data to tell it how many names it needs to create and where to store them (the output file).
We will always listen for errors and exits so that we know what is happening in our worker thread.
Now let’s see what the worker.js file does:
Basically, the code is the same as the original main.js file. Whenever we store a new name, we send it back to the main thread, so it keeps track of what’s going on inside the thread.
What was the result? We did the same thing in 1 minute and 24 seconds! That’s 37% faster than the single-threaded version!
Other use cases
Worker threads are a great solution when you need to perform a CPU-intensive task. They make filesystem related operations faster and can be of great help when you need to perform any type of concurrent operation. Best of all, as we mentioned earlier, they also work on single-core machines, so they guarantee better performance on any service.
In fact, I’ve used worker threads in a massive upload operation, where I need to check millions of users and store their data into a database. Using multi-threaded mode, the operation speed is 10 times faster than the corresponding single thread.
I also used worker threads in image manipulation. I need to build three thumbnails (of different sizes) for a single image, and multithreading also saves me time.
As you can see, the worker thread module is a great way to help you improve performance, so if it helps you in some way, let me know!