Хабрахабр

Релиз Node.js 10.5: мультипоточность из коробки

5. На прошлой неделе состоялся релиз Node.js версии 10. Сразу оговорюсь API находится в экспериментальной стадии и поэтому может измениться, но уже сейчас можно составить первое впечатление и получить представление о заложенных в его основу принципах и технологиях. 0, содержащий нововведение, чью значимость трудно переоценить, – поддержку многопоточности в виде модуля worker_threads. А если у вас есть желание, то и поучаствовать в финализации интерфейса, написании кода или исправлении багов (список issues).

История появления

По многим причинам такой подход не устраивает разработчиков, в частности потому, что это приводит к повтроной загрузке в память компьютера исполняемого кода Node.js со всеми встроенными модулями, что является неэффективным способом расходования ресурсов. На протяжении всей жизни Node.js единственным способом распараллелить вычисления был запуск нового процесса, например с использованим модуля cluster.

И пока разработчики искали с какой стороны подступиться к теме в вебе успешно был внедрен Worker API, который и стал ориентиром на начальных этапах. Тем неменее обсуждение внедрения многопоточности в Node.js всегда упиралось в сложность V8 и огромное количество неизвестных: как подключать нативные модули, разделять память, осуществлять коммуникацию между потоками и прочее. Разработка началась усилиями addaleax и была подхвачена сообществом.

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

Описание

Поэтому потоки будут поддерживать подключение всех доступных основному процессу модулей (на данный момент нативные модули не поддерживаются). Как уже было сказано выше, цель данной разработки – улучшение производительности при помощи распределенния нагрузки по раздельным потокам в рамках одного процесса, вместо запуска нескольких процессов.

При этом объекты вроде SharedArrayBuffer сохраняют свое поведение и не вызывают переаллокации. Так же как и в Worker API взаимодействие между главным и дочерним потоком осуществляется посредством передачи передаваемых (Transferrable) объектов посредством postMessage, что позволяет избежать проблем одновременного доступа, хоть и требует дополнительных обращений к памяти для копирования данных.

Из WebAPI был взят MessageChannel и MessagePort, что позволяет создавать изолированные каналы обмена сообщениями и передавать их между потоками.

Для того чтобы попробовать worker_threads в деле при запуске процесса необходимо указать специальный флаг:

node --experimental-worker main.js

Пример

Так как API еще может меняться я не буду его описывать, но приведу пример обмена сообщениями между родительским и дочерним потоком, в котором дочерний поток сообщает свой threadId, через MessagePort и завершает свою работу.

Главный поток

Пример кода основного потока:

// main.js
const = require('worker_threads'); const worker = new Worker(__dirname + '/worker.js'); worker.on('online', () => { console.log('Worker ready');
}); worker.on('message', (msg) => { console.log('Worker message:', msg);
}); worker.on('error', (err) => { console.error('Worker error:', err);
}); worker.on('exit', (code) => { console.log('Worker exit code:', code);
});

Дочерний поток

Таким образом сразу после выполнения кода из worker.js поток будет автоматически закрыт. Дочерний поток живет пока его очередь событий (event loop) не опустеет. Для связи с родителем используется parentPort:

// worker.js
const {threadId, parentPort} = require('worker_threads'); parentPort.postMessage(`Hello from thread #${threadId}.`);
// Exit happens here

В частности нет возможности отреагировать на сигналы SIGNINT, изменить значения process.env, а вызов process.exit остановит только worker, но не весь процесс. В дочернем потоке объект process переопределен, а его поведение несколько отличается от поведения process в родительском потоке.

Заключение

А так же позволят избежать платформозависимых ограничений вызванных различием Windows и Unix. Воркеры позволят сильно упростить создание приложений требующих взаимодействия между параллельно исполняемыми участками кода и, что особенно важно, делает коммуникацию и управление потоками наиболее очевидным способом. А пока продолжайте следить за изменениями и подключайтесь к процессу разработки API в репозитории. Уверен, что открывающиеся возможности привлекут новых разработчиков, которые еще не сделали выбор в пользу Node.js.

Ссылки

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть