preface

In general, Node.js is considered single-threaded. The main thread executes the program code step by step in the coded order. If the synchronous code blocks, the main thread is occupied, and the execution of the subsequent program code will be stuck. 🤔 yes, single threaded Node.js means that the main thread is “single threaded”.

To solve the problem of single-threading, worker_Threads, the protagonist of this article, emerged. Worker_threads first appeared in Node.js V10.5.0 as an experimental feature that requires an –experimental-worker command line to use. It will not be available until stable version V12.11.0.

This article will show you how to use Worker_Threads and how to execute Fibonacci numbers using Worker_Threads as a practical example.

A prerequisite for

To read and eat this article, you need to:

  • Installation of theNode. Js v12.11.0And above version
  • Basic knowledge of synchronous and asynchronous JavaScript programming
  • Learn how Node.js works

Worker_threads introduction

The Worker_Threads module allows threads that execute JavaScript in parallel.

Worker threads are useful for performing CPU-intensive JavaScript operations. They are not very helpful for I/O intensive work. Node.js’s built-in asynchronous I/O operations are more efficient than worker threads.

Unlike CHILD_process or cluster, worker_Threads can share memory. They are implemented by transferring an ArrayBuffer instance or by sharing a SharedArrayBuffer instance.

Worker_threads has proven to be the best solution to take full advantage of CPU performance due to the following features:

  1. They run a single process with multiple threads.
  2. Each thread executes an event loop.
  3. Each thread runs a single INSTANCE of the JS engine.
  4. Each thread executes a single Node.js instance.

How does Worker_Threads work

Worker_threads works by executing the script file specified by the main thread. Each thread executes in isolation from the other threads. However, these threads can pass messages back and forth through message channels.

The main thread uses the worker.postMessage() function to use the message channel, while the worker thread uses the parentport.postMessage () function.

Learn more from the official sample code:

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

if (isMainThread) {
  module.exports = function parseJSAsync(script) {
    return new Promise((resolve, reject) = > {
      const worker = new Worker(__filename, {
        workerData: script
      });
      worker.on('message', resolve);
      worker.on('error', reject);
      worker.on('exit'.(code) = > {
        if(code ! = =0)
          reject(new Error(`Worker stopped with exit code ${code}`));
      });
    });
  };
} else {
  const { parse } = require('some-js-parsing-library');
  const script = workerData;
  parentPort.postMessage(parse(script));
}
Copy the code

Both the main thread and the worker thread use the same file as the execution script (__filename is the path of the current execution file), and isMainThread is used to distinguish the main thread from the worker thread runtime logic. When the module’s exposed parseJSAsync method is called, a child worker thread is spawned to execute the call to the parse function.

Worker_threads specific usage

This section describes the use of Worker_Threads using specific examples

Create a workthread script file workerexample.js:

const { workerData, parentPort } = require('worker_threads')
parentPort.postMessage({ welcome: workerData })
Copy the code

Create main thread script file main.js:

const { Worker } = require('worker_threads')

const runWorker = (workerData) = > {
    return new Promise((resolve, reject) = > {
        // Import the workerexample.js' worker thread 'script file
        const worker = new Worker('./workerExample.js', { workerData });
        worker.on('message', resolve);
        worker.on('error', reject);
        worker.on('exit'.(code) = > {
            if(code ! = =0)
                reject(new Error(`stopped with  ${code} exit code`)); })})}const main = async() = > {const result = await runWorker('hello worker threads')
    console.log(result);
}

main().catch(err= > console.error(err))
Copy the code

Console command-line execution:

node main.js
Copy the code

Output:

{ welcome: 'hello worker threads' }
Copy the code

Worker_threads computes Fibonacci numbers

In this section, let’s look at a CPU-intensive example of generating Fibonacci numbers.

If you do this without a worker thread, the main thread will block as the NTH duration increases.

Create the worker thread script file worker.js

const {parentPort, workerData} = require("worker_threads");

parentPort.postMessage(getFibonacciNumber(workerData.num))

function getFibonacciNumber(num) {
    if (num === 0) {
        return 0;
    }
    else if (num === 1) {
        return 1;
    }
    else {
        return getFibonacciNumber(num - 1) + getFibonacciNumber(num - 2); }}Copy the code

Create main thread script file main.js:

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

let number = 30;

const worker = new Worker("./worker.js", {workerData: {num: number}});

worker.once("message".result= > {
    console.log(`${number}th Fibonacci Result: ${result}`);
});

worker.on("error".error= > {
    console.log(error);
});

worker.on("exit".exitCode= > {
    console.log(`It exited with code ${exitCode}`);
})

console.log("Execution in main thread");
Copy the code

Console command-line execution:

node main.js
Copy the code

Output:

Execution in main thread
30th Fibonacci Result: 832040
It exited with code 0
Copy the code

In the main.js file, we create a Worker thread from an instance of the class, the Worker as we saw in the previous example.

To get the results, we listen for three events,

  • messageThe responseThe worker threadSend a message.
  • exitinThe worker threadAn event that is triggered when execution is stopped.
  • errorTriggered when an error occurs.

We have main.js on the last line,

console.log("Execution in main thread");
Copy the code

From the console output, the main thread is not blocked by the Fibonacci sequence.

Therefore, as long as CPU intensive tasks are processed in the worker thread, we can continue to work on other tasks without worrying about blocking the main thread.

conclusion

Node.js has been criticized for its performance when handling CPU-intensive tasks. By effectively addressing these shortcomings, the introduction of worker threads improves node.js functionality.

For more information about Worker_Threads, visit its official documentation here.

thinking

Leave your thoughts at the end of this post, and I’ll add them in the comments section. Please join us at 👏.

  • worker_threadsAre threads recycled when they are idle?
  • worker_threadsHow to use shared memory?
  • Since said tothreadSo there should beThe thread pool?