Using Worker Threads in Node.js

Welcome back! Let's dive into how we can use worker threads in Node.js to perform parallel operations efficiently.

Setting Up Worker Threads

Step 1: Create a Worker Threads Example File

Start by creating a folder, ThreadsExample, and within it, create a file called threads-dogs.js.

Step 2: Importing the Worker Threads Module

To use worker threads, require the worker_threads module and destructure the necessary values from it:

const { Worker, isMainThread, workerData } = require('worker_threads');

Step 3: Checking the Execution Context

The isMainThread value helps us determine if the current code is running in the main thread or a worker thread. Using this, we can create new worker threads conditionally:

if (isMainThread) {
    console.log(`Main thread has process ID: ${process.pid}`);

    // Creating two worker threads
    new Worker(__filename, { workerData: [7, 6, 2, 3] });
    new Worker(__filename, { workerData: [1, 3, 4, 3] });
} else {
    console.log(`Worker thread has process ID: ${process.pid}`);
}

Here, __filename is a Node.js global variable that refers to the current file path. We pass it to the Worker constructor so that each worker runs the code within the same file.

Step 4: Avoid Infinite Worker Creation

To prevent recursive worker creation, ensure new workers are created only in the main thread by checking isMainThread.

Demonstrating Shared Process ID

Unlike the cluster module, which spawns separate processes, worker threads run within the same process. To see this:

if (isMainThread) {
    console.log(`Main thread has process ID: ${process.pid}`);
    new Worker(__filename, { workerData: [7, 6, 2, 3] });
    new Worker(__filename, { workerData: [1, 3, 4, 3] });
} else {
    console.log(`Worker thread has process ID: ${process.pid}`);
}

Running this code will output identical process IDs for the main thread and all worker threads.

Performing Parallel Operations

Now let's assign some work to the worker threads. Suppose we have arrays of numbers to sort. Sorting is a CPU-intensive operation, which makes it a great use case for worker threads.

Passing Data to Worker Threads

The Worker constructor accepts an object as a second parameter, where the workerData property allows us to send data to the worker:

if (isMainThread) {
    new Worker(__filename, { workerData: [7, 6, 2, 3] });
    new Worker(__filename, { workerData: [1, 3, 4, 3] });
} else {
    console.log(`Worker data: ${workerData}`);

    // Sorting the array
    const sortedData = workerData.sort((a, b) => a - b);
    console.log(`Sorted data: ${sortedData}`);
}

Output

When you run the program using node threads-dogs.js, you’ll see output like this:

Main thread has process ID: 12345
Worker data: [7, 6, 2, 3]
Sorted data: [2, 3, 6, 7]
Worker data: [1, 3, 4, 3]
Sorted data: [1, 3, 3, 4]

Explanation

  • The main thread sends an array to each worker thread using workerData.

  • Each worker processes the data independently by sorting the array.

  • The sorted arrays are logged to the console.

Key Advantages

  1. Parallel Execution: Each worker thread runs independently, taking advantage of multiple CPU cores.

  2. Shared Memory: All threads share the same process memory, reducing overhead compared to spawning separate processes.

  3. Efficient Resource Utilization: Worker threads provide a more efficient alternative to clustering for non-server-related tasks.

Summary

Using worker threads in Node.js allows us to execute JavaScript in parallel, optimizing CPU-bound tasks. While they’re not a replacement for clusters in server scenarios, worker threads are perfect for tasks like sorting, data processing, and mathematical computations.

0
Subscribe to my newsletter

Read articles from Muhammad Sufiyan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Muhammad Sufiyan
Muhammad Sufiyan

As a former 3D Animator with more than 12 years of experience, I have always been fascinated by the intersection of technology and creativity. That's why I recently shifted my career towards MERN stack development and software engineering, where I have been serving since 2021. With my background in 3D animation, I bring a unique perspective to software development, combining creativity and technical expertise to build innovative and visually engaging applications. I have a passion for learning and staying up-to-date with the latest technologies and best practices, and I enjoy collaborating with cross-functional teams to solve complex problems and create seamless user experiences. In my current role as a MERN stack developer, I have been responsible for developing and implementing web applications using MongoDB, Express, React, and Node.js. I have also gained experience in Agile development methodologies, version control with Git, and cloud-based deployment using platforms like Heroku and AWS. I am committed to delivering high-quality work that meets the needs of both clients and end-users, and I am always seeking new challenges and opportunities to grow both personally and professionally.