Як передати дані між потоками?
1. Як влаштована взаємодія між потоками
Кожен потік (Worker) у Node.js:
- має свій власний контекст (не ділить змінні з іншими),
- виконується в межах одного процесу,
- може обмінюватися повідомленнями з головним потоком через message passing (передачу повідомлень),
- за потреби - ділити пам'ять через
SharedArrayBuffer.
Тобто:
Потоки не бачать глобальних змінних одне одного, але можуть спілкуватися через спеціально надані канали.
2. Базовий спосіб - передача повідомлень (message passing)
Це найпоширеніший і найбезпечніший спосіб обміну даними між потоками.
Приклад:
main.js
const { Worker } = require('worker_threads');
const worker = new Worker('./worker.js');
// Надсилаємо дані в потік
worker.postMessage({ name: 'Tim', numbers: [1, 2, 3] });
// Отримуємо відповідь
worker.on('message', (result) => {
console.log('Відповідь від потоку:', result);
});worker.js
const { parentPort } = require('worker_threads');
// Отримуємо повідомлення від основного потоку
parentPort.on('message', (data) => {
const sum = data.numbers.reduce((a, b) => a + b, 0);
parentPort.postMessage({ greeting: `Привіт, ${data.name}!`, sum });
});Як це працює:
main.jsстворює потік і надсилає дані за допомогоюpostMessage().- Потік отримує дані через
parentPort.on('message', ...). - Після обчислень - надсилає відповідь назад через
parentPort.postMessage().
3. Передача даних при запуску - workerData
Ти можеш передати дані один раз під час створення потоку, використовуючи параметр workerData.
Приклад:
main.js
const { Worker } = require('worker_threads');
const worker = new Worker('./worker.js', {
workerData: { n: 5 }
});
worker.on('message', result => console.log('Результат:', result));worker.js
const { workerData, parentPort } = require('worker_threads');
let fact = 1;
for (let i = 1; i <= workerData.n; i++) fact *= i;
parentPort.postMessage({ factorial: fact });Тут дані (n: 5) передаються одразу під час створення воркера, без postMessage().
4. Спільне використання пам'яті - SharedArrayBuffer
Це просунутий спосіб, що дозволяє ділити пам'ять між потоками, щоб уникнути копіювання даних і прискорити обмін.
Використовується, коли:
- потрібно часто обмінюватися великими масивами,
- важлива максимальна продуктивність,
- допустимі низькорівневі операції.
Приклад:
main.js
const { Worker } = require('worker_threads');
const shared = new SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT);
const sharedArray = new Int32Array(shared);
sharedArray[0] = 42;
const worker = new Worker('./worker.js', { workerData: shared });
worker.on('exit', () => {
console.log('Змінено воркером:', sharedArray[0]); // 84
});worker.js
const { workerData } = require('worker_threads');
const arr = new Int32Array(workerData);
arr[0] *= 2; // Модифікуємо спільне значенняТут:
SharedArrayBufferстворює спільну область пам'яті, доступну обом потокам.- Зміни, зроблені в одному потоці, видно в іншому негайно.
- Можна використовувати
Atomicsдля безпечних операцій (щоб уникнути гонок даних).
5. Передача великих даних без копіювання - Transferable objects
Node.js підтримує передачу "за посиланням" деяких об'єктів (замість копіювання),
наприклад ArrayBuffer, MessagePort, SharedArrayBuffer.
Приклад передачі великого буфера без копіювання:
const { Worker } = require('worker_threads');
const buffer = new ArrayBuffer(8);
const worker = new Worker('./worker.js');
worker.postMessage(buffer, [buffer]); // передача за посиланням (buffer тепер недоступний у main)
worker.on('message', () => console.log('Воркер отримав буфер!'));worker.js
const { parentPort } = require('worker_threads');
parentPort.on('message', (buffer) => {
console.log('Буфер отримано у воркері, розмір:', buffer.byteLength);
parentPort.postMessage('OK');
});Після передачі об'єкт стає "detached" (недоступний у відправника).
6. Зведення способів передачі даних
| Спосіб | Напрямок | Особливості |
|---|---|---|
postMessage() / on('message') | двосторонньо | Найпоширеніший спосіб, безпечний |
workerData | у воркер під час створення | Одноразова передача при запуску |
SharedArrayBuffer | спільна пам'ять | Дуже швидко, але вимагає обережності |
Transferable objects | одноразова передача за посиланням | Без копіювання, після передачі джерело втрачає доступ |
7. Як обрати спосіб
| Сценарій | Що використати |
|---|---|
| Звичайні дані (об'єкти, JSON, числа) | postMessage() |
| Передача параметрів при запуску | workerData |
| Обмін великою кількістю даних | SharedArrayBuffer |
| Максимальна продуктивність, без копіювання | Transferable objects |
| Кілька воркерів, незалежні задачі | postMessage() + черги повідомлень |
Підсумок
| Характеристика | Значення |
|---|---|
| Потоки ізольовані одне від одного | Так |
| Спільні змінні доступні? | Ні |
| Як спілкуються | Через message passing або спільну пам'ять |
| Передача при старті | Через workerData |
| Розділювана пам'ять | SharedArrayBuffer + Atomics |
| Безпечний варіант | postMessage() |
Висновок:
У Node.js потоки не діляться станом напряму.
Для обміну даними використовують:
- повідомлення (postMessage / parentPort) - безпечний, простий спосіб;
- спільну пам'ять (SharedArrayBuffer) - швидкий, але вимагає акуратності;
- transferable об'єкти - для ефективної передачі великих структур.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.