The basic information

The author tested this feature on the latest development version of Node.js, v11.4.0. Currently, it is necessary to add flags to introduce Worker Threads, for example:

node --experimental-worker index.js
Copy the code

The Worker Threads feature was introduced in v10.5.0 on June 20, 2018:

node/CHANGELOG

At present, the module is in the Stability 1-experimental stage, which will undergo large changes and is not recommended to be used in production environment.

Module interface

const {
  isMainThread, parentPort, workerData, threadId,
  MessageChannel, MessagePort, Worker
} = require('worker_threads');
Copy the code

The module has very few objects and classes, only four objects and three classes.

  • isMainThread: false indicates the current worker thread,falseRepresents the main thread
  • parentPort: refers to the parent process in worker threadsMessagePortObject of type, as in the main threadnull
  • workerDataIn the worker thread, is the initialization data of the parent process when creating the worker thread. In the main thread, is the initialization data of the parent processundefined
  • threadId: is the thread ID in worker threads and is the thread ID in parent processes0.
  • MessageChannel: contains two threads that can already communicate with each otherMessagePortType object that can be used to create a custom communication channel, as shown in Example 2.
  • MessagePort: handle for cross-thread communication, inheritedEventEmitter, including the Close Message event to receive messages that an object closes and sends, and operations such as close postMessage.
  • WorkerThe main thread is the object type used to create worker threads, including all MessagePort operations and some unique meta data operations for child threads. The first argument to the constructor is an entry script executed by the child thread, and the second argument contains configuration items that specify some initial arguments. For details, seeThe document

Changes to the memory model

SharedArrayBuffer is typically used when using Cluster and Child_process to implement memory that needs to be shared by multiple processes.

port.postMessage(value[, transferList])
Copy the code

Now the Worker Threads module does not recommend multithreading shared memory at the API layer. The value of the first parameter is cloned by the thread receiving the message. TransferList can only pass an ArrayBuffer or a MessagePort object. Passing an ArrayBuffer modifies the access permission of the Buffer to the thread receiving the message. For example, see Example 2.

All cross-thread message communication is implemented through the low-level V8 serialization, with the more specific Worker Threads and V8 multi-threading models and their relationship to the browser’s Web Worker standard being left unexplored.

Example 1: Count the communication between the main thread and the worker thread. When the count reaches 5, the worker thread commits suicide

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

function mainThread() {
  const worker = new Worker(__filename, { workerData: 0 });
  worker.on('exit', code => { console.log(`main: worker stopped with exit code ${code}`); });
  worker.on('message', msg => {
    console.log(`main: receive ${msg}`);
    worker.postMessage(msg + 1);
  });
}

function workerThread() {
  console.log(`worker: threadId ${threadId} start with ${__filename}`);
  console.log(`worker: workerDate ${workerData}`);
  parentPort.on('message', msg => {
    console.log(`worker: receive ${msg}`);
    if (msg === 5) { process.exit(); }
    parentPort.postMessage(msg);
  }),
  parentPort.postMessage(workerData);
}

if (isMainThread) {
  mainThread();
} else {
  workerThread();
}
Copy the code

Output result:

worker: threadId 1 start with /Users/azard/test/index.js
worker: workerDate 0
main: receive 0
worker: receive 1
main: receive 1
worker: receive 2
main: receive 2
worker: receive 3
main: receive 3
worker: receive 4
main: receive 4
worker: receive 5
main: receive 5
main: worker stopped with exit code 0
Copy the code

Example 2: Use a MessageChannel to let two child threads communicate directly

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

if (isMainThread) {
  const worker1 = new Worker(__filename);
  const worker2 = new Worker(__filename);
  const subChannel = new MessageChannel();
  worker1.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]);
  worker2.postMessage({ hereIsYourPort: subChannel.port2 }, [subChannel.port2]);
} else {
  parentPort.once('message', (value) => {
    value.hereIsYourPort.postMessage('hello');
    value.hereIsYourPort.on('message', msg => {
      console.log(`thread ${threadId}: receive ${msg}`);
    });
  });
}
Copy the code

Output:

thread 2: receive hello
thread 1: receive hello
Copy the code

conclusion

Now that Node.js has true multithreading, there is no need to use cluster or child_process to handle some problems, and the related framework and library model can also start to iterate after Worker Threads stabilize.