Модуль worker_threads Node.js

Java FileВстроенные модули Node.js


Node.jsworker_threadsМодуль позволяет разработчикам создавать несколько потоков в приложениях Node.js. В отличие от традиционной однопоточной модели Node.js, этот модуль позволяет выполнять код JavaScript в отдельных потоках, обеспечивая настоящую параллельную обработку.

Зачем нужна многопоточность

Node.js по умолчанию однопоточен, что означает:

  1. Задачи с высокой нагрузкой на CPU будут блокировать цикл событий
  2. Невозможность полностью использовать производительность многоядерных CPU
  3. Длительные вычисления снизят отзывчивость приложения

worker_threadsМодуль решает эти проблемы, позволяя переносить задачи с высокой нагрузкой на CPU в другие потоки.


Базовые понятия

Основной поток против рабочего потока

  1. Основной поток: основной поток выполнения приложения, отвечающий за создание и управление рабочими потоками.
  2. Рабочий поток: независимый поток выполнения, созданный основным потоком и используемый для выполнения конкретных задач.

Взаимодействие между потоками

Рабочие потоки и основной поток взаимодействуют посредством передачи сообщений, а не через общую память. Такой подход позволяет избежать сложных проблем синхронизации.


Основной API

Основные компоненты модуля worker_threads

Пример

const {
  Worker,          // Класс, используемый для создания новых потоков
  isMainThread,    // Определяет, является ли текущий контекст главным потоком
  parentPort,      // Используется для связи с родительским потоком
  workerData       // Данные, передаваемые при создании потока
} = require('worker_threads');

Пример использования

Создание простого рабочего потока

Код главного потока (main.js):

Пример

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

if (isMainThread) {
  // Код главного потока
  console.log('Это главный поток');
 
  // Создать рабочий поток
  const worker = new Worker('./worker.js', {
    workerData: {
      message: 'Привет, рабочий поток!'
    }
  });
 
  // Слушаем сообщения из рабочего потока
  worker.on('message', (msg) => {
    console.log(`Получено от рабочего потока: ${msg}`);
  });
 
  worker.on('error', (err) => {
    console.error('Ошибка рабочего потока:', err);
  });
 
  worker.on('exit', (code) => {
    if (code !== 0) {
      console.error(`Рабочий поток неожиданно завершился, код выхода: ${code}`);
    }
  });
} else {
  // Код рабочего потока
  require('./worker.js');
}

Код рабочего потока (worker.js):

Пример

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

console.log('Это рабочий поток');
console.log('Получено сообщение от главного потока:', workerData.message);

// Имитируем некоторую работу
setTimeout(() => {
  // Отправляем сообщение главному потоку
  parentPort.postMessage('Работа завершена!');
}, 2000);

Продвинутое использование

Передача сложных данных

Пример

// Главный поток
const worker = new Worker('./worker.js', {
  workerData: {
    array: new Uint8Array([1, 2, 3, 4]),
    object: { key: 'value' }
  }
});

// Рабочий поток
console.log(workerData.array);  // Uint8Array [ 1, 2, 3, 4 ]
console.log(workerData.object); // { key: 'value' }

Разделяемая память (SharedArrayBuffer)

Пример

// Главный поток
const sharedBuffer = new SharedArrayBuffer(16);
const arr = new Uint8Array(sharedBuffer);
arr[0] = 1;

const worker = new Worker('./worker.js', {
  workerData: { sharedBuffer }
});

// Рабочий поток
const { sharedBuffer } = workerData;
const arr = new Uint8Array(sharedBuffer);
console.log(arr[0]); // 1

Лучшие практики

  1. Разумное использование: Не создавайте поток для каждой мелкой задачи; создание потоков имеет накладные расходы.
  2. Обработка ошибок: Всегда отслеживайте события 'error' и 'exit' рабочих потоков.
  3. Очистка ресурсов: Убедитесь, что рабочие потоки корректно завершаются после выполнения своих задач.
  4. Оптимизация обмена данными: Минимизируйте объём данных, передаваемых при межпотоковом взаимодействии.
  5. Привязка к ЦП: Для длительно работающих потоков рассмотрите возможность настройки привязки к ЦП.

Соображения производительности

  1. Пул потоков: Для частых краткосрочных задач рассмотрите использование паттерна пула потоков.
  2. Балансировка нагрузки: Равномерно распределяйте рабочие потоки по многоядерным системам.
  3. Использование памяти: Каждый рабочий поток имеет собственный экземпляр V8 и собственное пространство памяти.

Часто задаваемые вопросы

Когда следует использовать worker_threads?

  • Интенсивные вычисления на CPU
  • Необходимо обрабатывать несколько задач параллельно
  • Необходимость задействовать многоядерные CPU

В чём разница между worker_threads и модулем cluster?

  • clusterСоздаёт несколько процессов Node.js
  • worker_threadsСоздаёт потоки, совместно использующие один и тот же процесс

Как отлаживать рабочие потоки?

Используйте--inspectи--inspect-brkфлаг, и назначьте каждому рабочему потоку отдельный порт отладки.

Пример

node --inspect=9229 main.js

Итог

Node.jsworker_threadsМодуль привносит в JavaScript настоящие возможности многопоточности, позволяя разработчикам лучше использовать производительность современных многоядерных ЦП. При правильном использовании рабочих потоков можно значительно повысить производительность приложений с интенсивной нагрузкой на ЦП, сохраняя при этом преимущества неблокирующего ввода-вывода Node.js.

Java FileВстроенные модули Node.js

Другие расширения