Предварительное исследование многопоточных рабочих потоков Node.js

Node.js

Основная информация

Автор протестировал эту функцию на последней разрабатываемой версии Node.js v11.4.0. В настоящее время необходимо добавить флаг, чтобы представить ее.Worker Threads,Например:

node --experimental-worker index.js

Worker ThreadsФункции были представлены в версии 10.5.0 20 июня 2018 г.:

node/CHANGELOG

В настоящее время модуль находится вStability 1 - Experimentalэтап, изменения будут больше,не предлагаетсяДля производственной среды.

интерфейс модуля

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

В этом модуле очень мало объектов и классов, всего 4 объекта и 3 класса.

  • isMainThread: false означает, что это в настоящее время рабочая нить,falseПредставляет основной поток
  • parentPort: в рабочем потоке представляет родительский процесс.MessagePortобъект типа, в основном потоке какnull
  • workerData: В рабочем потоке это данные инициализации, когда родительский процесс создает рабочий поток, в основном потоке этоundefined
  • threadId: идентификатор потока в рабочем потоке, идентификатор потока в родительском процессе.0.
  • MessageChannel: Содержит два потока, которые уже могут взаимодействовать друг с другом.MessagePortТиповые объекты, которые можно использовать для создания пользовательских каналов связи, см. примеры реализации.
  • MessagePort: дескриптор межпотокового взаимодействия, унаследованныйEventEmitter, включая событие close message для получения сообщений о закрытии и отправке объектов, а также такие операции, как close postMessage.
  • Worker: Тип объекта, используемый основным потоком для создания рабочих потоков, включая все операции MessagePort и некоторые определенные операции с метаданными подпотока. Первый параметр конструктора — это программа-скрипт входа, выполняемая дочерним потоком, а второй параметр содержит некоторые элементы конфигурации, которые могут указывать некоторые начальные параметры. Подробнее см.Документация

Изменения в модели памяти

в настоящее время используетclusterиchild_processВо время нормального использованияSharedArrayBufferДля реализации памяти, которая должна совместно использоваться несколькими процессами.

port.postMessage(value[, transferList])

Теперь модуль Worker Threads не рекомендует использовать многопотоковую разделяемую память на уровне API, значение первого значения параметра будет клонировано в поток, получающий сообщение. TransferList может быть передан толькоArrayBufferилиMessagePortобъект, прошелArrayBufferИзменяет права доступа к буферу для потока, который принимает сообщение, передаваяMessagePortВы можете обратиться ко второму примеру.

Все коммуникационные сообщения между потоками сериализуются для достижения базовой версии 8, отношений, стандартов Web Worker, более конкретных рабочих потоков и моделей v8, а также многопоточного браузера и еще не запущены.

Пример 1: счетчик связи между основным потоком и рабочим потоком, после того как счетчик достигает 5, рабочий поток совершает самоубийство

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();
}

Выходной результат:

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

Пример 2. Используйте MessageChannel, чтобы позволить двум дочерним потокам взаимодействовать напрямую

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}`);
    });
  });
}

вывод:

thread 2: receive hello
thread 1: receive hello

Суммировать

Теперь, когда Node.js имеет настоящую многопоточность, нет необходимости использовать кластер или многопроцессорный процесс child_process для решения некоторых проблем.Связанная модель фреймворка и библиотеки также может итеративно обновляться после того, как рабочие потоки станут стабильными.