Модуль worker_threads Node.js
Node.jsworker_threadsМодуль позволяет разработчикам создавать несколько потоков в приложениях Node.js. В отличие от традиционной однопоточной модели Node.js, этот модуль позволяет выполнять код JavaScript в отдельных потоках, обеспечивая настоящую параллельную обработку.
Зачем нужна многопоточность
Node.js по умолчанию однопоточен, что означает:
- Задачи с высокой нагрузкой на CPU будут блокировать цикл событий
- Невозможность полностью использовать производительность многоядерных CPU
- Длительные вычисления снизят отзывчивость приложения
worker_threadsМодуль решает эти проблемы, позволяя переносить задачи с высокой нагрузкой на CPU в другие потоки.
Базовые понятия
Основной поток против рабочего потока
- Основной поток: основной поток выполнения приложения, отвечающий за создание и управление рабочими потоками.
- Рабочий поток: независимый поток выполнения, созданный основным потоком и используемый для выполнения конкретных задач.
Взаимодействие между потоками
Рабочие потоки и основной поток взаимодействуют посредством передачи сообщений, а не через общую память. Такой подход позволяет избежать сложных проблем синхронизации.
Основной API
Основные компоненты модуля worker_threads
Пример
Worker, // Класс, используемый для создания новых потоков
isMainThread, // Определяет, является ли текущий контекст главным потоком
parentPort, // Используется для связи с родительским потоком
workerData // Данные, передаваемые при создании потока
} = require('worker_threads');
Пример использования
Создание простого рабочего потока
Код главного потока (main.js):
Пример
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):
Пример
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
Лучшие практики
- Разумное использование: Не создавайте поток для каждой мелкой задачи; создание потоков имеет накладные расходы.
- Обработка ошибок: Всегда отслеживайте события 'error' и 'exit' рабочих потоков.
- Очистка ресурсов: Убедитесь, что рабочие потоки корректно завершаются после выполнения своих задач.
- Оптимизация обмена данными: Минимизируйте объём данных, передаваемых при межпотоковом взаимодействии.
- Привязка к ЦП: Для длительно работающих потоков рассмотрите возможность настройки привязки к ЦП.
Соображения производительности
- Пул потоков: Для частых краткосрочных задач рассмотрите использование паттерна пула потоков.
- Балансировка нагрузки: Равномерно распределяйте рабочие потоки по многоядерным системам.
- Использование памяти: Каждый рабочий поток имеет собственный экземпляр V8 и собственное пространство памяти.
Часто задаваемые вопросы
Когда следует использовать worker_threads?
- Интенсивные вычисления на CPU
- Необходимо обрабатывать несколько задач параллельно
- Необходимость задействовать многоядерные CPU
В чём разница между worker_threads и модулем cluster?
clusterСоздаёт несколько процессов Node.jsworker_threadsСоздаёт потоки, совместно использующие один и тот же процесс
Как отлаживать рабочие потоки?
Используйте--inspectи--inspect-brkфлаг, и назначьте каждому рабочему потоку отдельный порт отладки.
Пример
Итог
Node.jsworker_threadsМодуль привносит в JavaScript настоящие возможности многопоточности, позволяя разработчикам лучше использовать производительность современных многоядерных ЦП. При правильном использовании рабочих потоков можно значительно повысить производительность приложений с интенсивной нагрузкой на ЦП, сохраняя при этом преимущества неблокирующего ввода-вывода Node.js.
Встроенные модули Node.js