Для создания полнофункциональных многопоточных приложений в Node.js используется модуль worker_threads.
Модуль node:worker_threads позволяет использовать потоковые воркеры (thread worker) — фрагменты кода, обычно извлекаемые из файла и выполняемые в отдельном потоке (реализация параллельного исполнения JavaScript).
Для доступа к node:worker_threads его необходимо импортировать:
1 2 3 |
const worker = require('node:worker_threads'); // ИЛИ, например import { Worker, workerData, parentPort } from 'node:worker_threads' |
Особенности worker threads в Node.js:
- Рабочие потоки (worker threads) полезны для выполнения операций JavaScript с интенсивным использованием процессора, однако следует учитывать, что в Node.js встроенные асинхронные операции ввода-вывода более эффективны, чем реализованные с worker threads.
- В отличие от child_process или cluster, worker_threads может совместно использовать память путем передачи экземпляров ArrayBuffer или совместного использования экземпляров SharedArrayBuffer.
Некоторые свойства модуля worker_threads:
- isMainThread - true, если код не работает внутри дочернего потока воркера;
- workerData - содержит данные, включённые в конструктор воркера созданным потоком;
- parentPort - экземпляр класса MessagePort, используется для связи с родительским потоком;
- threadId - уникальный идентификатор, присвоенный воркеру.
Пример свойства isMainThread
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
console.log("isMainThread _1", isMainThread) // true if (isMainThread) { // Перезагрузим текущий файл внутри экземпляра Worker new Worker(__filename); console.log("isMainThread _2", isMainThread) // true } else { console.log('Inside Worker!'); console.log("isMainThread _3", isMainThread) // false } // Вывод в консоль (в скобках - пояснения): // isMainThread _1 true (worker НЕ запущен, дочерних потоков нет) // isMainThread _2 true (worker ЗАПУЩЕН, но вывод из основного потока) // isMainThread _1 false (worker ЗАПУЩЕН, вывод из дочернего потока) // Inside Worker! (worker ЗАПУЩЕН, вывод из дочернего потока) // isMainThread _3 false (worker ЗАПУЩЕН, вывод из дочернего потока) |
[свернуть]
Пример работы с workerData
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
console.log("isMainThread _1", isMainThread) // true if (isMainThread) { // Перезагрузим текущий файл внутри экземпляра Worker, // передав в него workerData: 'Hello, world!' new Worker(__filename, { workerData: 'Hello, world!' }); console.log("isMainThread _2", isMainThread) // true } else { console.log('Inside Worker!'); console.log("isMainThread _3", isMainThread) // false console.log('WorkerData', workerData); // Hello, world! } // Вывод в консоль: // isMainThread _1 true // isMainThread _2 true // isMainThread _1 false (worker ЗАПУЩЕН, вывод из дочернего потока) // Inside Worker! (worker ЗАПУЩЕН, вывод из дочернего потока) // isMainThread _3 false (worker ЗАПУЩЕН, вывод из дочернего потока) // WorkerData Hello, world! (worker ЗАПУЩЕН, вывод из дочернего потока) |
Или передадим в workerData, например, результат выполнения другой функции:
1 2 3 4 5 6 7 8 9 10 11 |
function setWorkerData() { return 'This data from setWorkerData()' } if (isMainThread) { // Перезагрузим текущий файл внутри экземпляра Worker, // передав в него workerData: setWorkerData() new Worker(__filename, { workerData: setWorkerData() }); } else { console.log('WorkerData:', workerData); // WorkerData: This data from setWorkerData() } |
[свернуть]
Пример использования свойства worker.parentPort
Если поток является потоком Worker, то worker.parentPort разрешает связь с родительским потоком, при этом:
- сообщения, отправленные из дочернего потока с помощью parentPort.postMessage(), доступны в родительском потоке с помощью worker.on('message');
- сообщения, отправленные из родительского потока с помощью worker.postMessage(), доступны в дочернем потоке с помощью parentPort.on('message').
Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
if (isMainThread) { // создаем дочерний поток из файла __filename const worker = new Worker(__filename); worker.once('message', (message) => { console.log('Получили в родительский поток: ', message); }); // Отправляем данные из родительского потока в дочерний поток worker.postMessage('Hello, world!'); } else { // Когда данные из родительского потока получены, // обрабатываем и отправляем результат обратно в родительский parentPort.once('message', (message) => { message = "\n результат обработки в дочернем - " + message.toUpperCase() parentPort.postMessage(message); }); } // Получили в родительский поток: // результат обработки в дочернем - HELLO, WORLD! |
[свернуть]