Thread di lavoro
[Stabile: 2 - Stabile]
Stabile: 2 Stabilità: 2 - Stabile
Codice sorgente: lib/worker_threads.js
Il modulo node:worker_threads
abilita l'uso di thread che eseguono JavaScript in parallelo. Per accedervi:
const worker = require('node:worker_threads');
I worker (thread) sono utili per eseguire operazioni JavaScript ad alta intensità di CPU. Non aiutano molto con il lavoro ad alta intensità di I/O. Le operazioni di I/O asincrone integrate di Node.js sono più efficienti di quanto possano esserlo i worker.
A differenza di child_process
o cluster
, worker_threads
possono condividere la memoria. Lo fanno trasferendo istanze di ArrayBuffer
o condividendo istanze di SharedArrayBuffer
.
const {
Worker, isMainThread, parentPort, workerData,
} = require('node:worker_threads');
if (isMainThread) {
module.exports = function parseJSAsync(script) {
return new Promise((resolve, reject) => {
const worker = new Worker(__filename, {
workerData: script,
});
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0)
reject(new Error(`Worker si è fermato con codice di uscita ${code}`));
});
});
};
} else {
const { parse } = require('some-js-parsing-library');
const script = workerData;
parentPort.postMessage(parse(script));
}
L'esempio precedente genera un thread Worker per ogni chiamata parseJSAsync()
. In pratica, utilizza un pool di worker per questo tipo di attività. Altrimenti, il sovraccarico della creazione di worker probabilmente supererebbe i loro vantaggi.
Quando implementi un pool di worker, utilizza l'API AsyncResource
per informare gli strumenti di diagnostica (ad es. per fornire stack trace asincroni) sulla correlazione tra le attività e i loro risultati. Consulta "Utilizzo di AsyncResource
per un pool di thread Worker
" nella documentazione di async_hooks
per un esempio di implementazione.
I thread di lavoro ereditano le opzioni non specifiche del processo per impostazione predefinita. Fai riferimento a Opzioni del costruttore Worker
per sapere come personalizzare le opzioni dei thread di lavoro, in particolare le opzioni argv
e execArgv
.
worker.getEnvironmentData(key)
[Cronologia]
Versione | Modifiche |
---|---|
v17.5.0, v16.15.0 | Non è più sperimentale. |
v15.12.0, v14.18.0 | Aggiunto in: v15.12.0, v14.18.0 |
key
<any> Qualsiasi valore JavaScript arbitrario e clonabile che può essere utilizzato come chiave di <Map>.- Restituisce: <any>
All'interno di un thread worker, worker.getEnvironmentData()
restituisce un clone dei dati passati a worker.setEnvironmentData()
del thread di generazione. Ogni nuovo Worker
riceve automaticamente la propria copia dei dati ambientali.
const {
Worker,
isMainThread,
setEnvironmentData,
getEnvironmentData,
} = require('node:worker_threads');
if (isMainThread) {
setEnvironmentData('Hello', 'World!');
const worker = new Worker(__filename);
} else {
console.log(getEnvironmentData('Hello')); // Stampa 'World!'.
}
worker.isMainThread
Aggiunto in: v10.5.0
È true
se questo codice non è in esecuzione all'interno di un thread Worker
.
const { Worker, isMainThread } = require('node:worker_threads');
if (isMainThread) {
// Questo ricarica il file corrente all'interno di un'istanza Worker.
new Worker(__filename);
} else {
console.log('Inside Worker!');
console.log(isMainThread); // Stampa 'false'.
}
worker.markAsUntransferable(object)
Aggiunto in: v14.5.0, v12.19.0
object
<any> Qualsiasi valore JavaScript arbitrario.
Contrassegna un oggetto come non trasferibile. Se object
si verifica nell'elenco di trasferimento di una chiamata port.postMessage()
, viene generato un errore. Questa è una no-op se object
è un valore primitivo.
In particolare, questo ha senso per gli oggetti che possono essere clonati, piuttosto che trasferiti, e che sono utilizzati da altri oggetti sul lato mittente. Ad esempio, Node.js contrassegna gli ArrayBuffer
che utilizza per il suo Buffer
pool con questo.
Questa operazione non può essere annullata.
const { MessageChannel, markAsUntransferable } = require('node:worker_threads');
const pooledBuffer = new ArrayBuffer(8);
const typedArray1 = new Uint8Array(pooledBuffer);
const typedArray2 = new Float64Array(pooledBuffer);
markAsUntransferable(pooledBuffer);
const { port1 } = new MessageChannel();
try {
// Questo genererà un errore, perché pooledBuffer non è trasferibile.
port1.postMessage(typedArray1, [ typedArray1.buffer ]);
} catch (error) {
// error.name === 'DataCloneError'
}
// La seguente riga stampa il contenuto di typedArray1 -- possiede ancora
// la sua memoria e non è stato trasferito. Senza
// `markAsUntransferable()`, questo stamperebbe un Uint8Array vuoto e la
// chiamata postMessage sarebbe andata a buon fine.
// Anche typedArray2 è intatto.
console.log(typedArray1);
console.log(typedArray2);
Non esiste un equivalente di questa API nei browser.
worker.isMarkedAsUntransferable(object)
Aggiunto in: v21.0.0
Verifica se un oggetto è contrassegnato come non trasferibile con markAsUntransferable()
.
const { markAsUntransferable, isMarkedAsUntransferable } = require('node:worker_threads');
const pooledBuffer = new ArrayBuffer(8);
markAsUntransferable(pooledBuffer);
isMarkedAsUntransferable(pooledBuffer); // Restituisce true.
Non esiste un equivalente a questa API nei browser.
worker.markAsUncloneable(object)
Aggiunto in: v23.0.0
object
<any> Qualsiasi valore JavaScript arbitrario.
Contrassegna un oggetto come non clonabile. Se object
viene utilizzato come message
in una chiamata a port.postMessage()
, viene generato un errore. Questa è un'operazione no-op se object
è un valore primitivo.
Questo non ha alcun effetto su ArrayBuffer
o su qualsiasi oggetto simile a Buffer
.
Questa operazione non può essere annullata.
const { markAsUncloneable } = require('node:worker_threads');
const anyObject = { foo: 'bar' };
markAsUncloneable(anyObject);
const { port1 } = new MessageChannel();
try {
// Questo genererà un errore, perché anyObject non è clonabile.
port1.postMessage(anyObject);
} catch (error) {
// error.name === 'DataCloneError'
}
Non esiste un equivalente a questa API nei browser.
worker.moveMessagePortToContext(port, contextifiedSandbox)
Aggiunto in: v11.13.0
port
<MessagePort> La porta del messaggio da trasferire.contextifiedSandbox
<Object> Un oggetto contestualizzato come restituito dal metodovm.createContext()
.- Restituisce: <MessagePort>
Trasferisce una MessagePort
a un vm
Context diverso. L'oggetto port
originale viene reso inutilizzabile e l'istanza MessagePort
restituita prende il suo posto.
La MessagePort
restituita è un oggetto nel contesto di destinazione ed eredita dalla sua classe globale Object
. Gli oggetti passati al listener port.onmessage()
vengono anch'essi creati nel contesto di destinazione ed ereditano dalla sua classe globale Object
.
Tuttavia, la MessagePort
creata non eredita più da EventTarget
e solo port.onmessage()
può essere utilizzata per ricevere eventi utilizzando essa.
worker.parentPort
Aggiunto in: v10.5.0
Se questo thread è un Worker
, questo è un MessagePort
che consente la comunicazione con il thread padre. I messaggi inviati usando parentPort.postMessage()
sono disponibili nel thread padre usando worker.on('message')
e i messaggi inviati dal thread padre usando worker.postMessage()
sono disponibili in questo thread usando parentPort.on('message')
.
const { Worker, isMainThread, parentPort } = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
worker.once('message', (message) => {
console.log(message); // Stampa 'Hello, world!'.
});
worker.postMessage('Hello, world!');
} else {
// Quando viene ricevuto un messaggio dal thread padre, lo rimanda indietro:
parentPort.once('message', (message) => {
parentPort.postMessage(message);
});
}
worker.postMessageToThread(threadId, value[, transferList][, timeout])
Aggiunto in: v22.5.0
[Stabile: 1 - Sperimentale]
Stabile: 1 Stabilità: 1.1 - Sviluppo attivo
threadId
<number> L'ID del thread di destinazione. Se l'ID del thread non è valido, verrà generato un erroreERR_WORKER_MESSAGING_FAILED
. Se l'ID del thread di destinazione è l'ID del thread corrente, verrà generato un erroreERR_WORKER_MESSAGING_SAME_THREAD
.value
<any> Il valore da inviare.transferList
<Object[]> Se uno o più oggetti simili aMessagePort
vengono passati invalue
, è necessario untransferList
per tali elementi oppure viene generatoERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
. Vedereport.postMessage()
per maggiori informazioni.timeout
<number> Tempo di attesa in millisecondi per la consegna del messaggio. Per impostazione predefinita èundefined
, il che significa attendere per sempre. Se l'operazione scade, viene generato un erroreERR_WORKER_MESSAGING_TIMEOUT
.- Restituisce: <Promise> Una promise che viene risolta se il messaggio è stato elaborato correttamente dal thread di destinazione.
Invia un valore a un altro worker, identificato dal suo ID thread.
Se il thread di destinazione non ha alcun listener per l'evento workerMessage
, l'operazione genererà un errore ERR_WORKER_MESSAGING_FAILED
.
Se il thread di destinazione ha generato un errore durante l'elaborazione dell'evento workerMessage
, l'operazione genererà un errore ERR_WORKER_MESSAGING_ERRORED
.
Questo metodo deve essere utilizzato quando il thread di destinazione non è il genitore o il figlio diretto del thread corrente. Se i due thread sono padre-figlio, utilizzare require('node:worker_threads').parentPort.postMessage()
e worker.postMessage()
per consentire ai thread di comunicare.
L'esempio seguente mostra l'uso di postMessageToThread
: crea 10 thread nidificati, l'ultimo dei quali tenterà di comunicare con il thread principale.
import { fileURLToPath } from 'node:url';
import process from 'node:process';
import {
postMessageToThread,
threadId,
workerData,
Worker,
} from 'node:worker_threads';
const channel = new BroadcastChannel('sync');
const level = workerData?.level ?? 0;
if (level < 10) {
const worker = new Worker(fileURLToPath(import.meta.url), {
workerData: { level: level + 1 },
});
}
if (level === 0) {
process.on('workerMessage', (value, source) => {
console.log(`${source} -> ${threadId}:`, value);
postMessageToThread(source, { message: 'pong' });
});
} else if (level === 10) {
process.on('workerMessage', (value, source) => {
console.log(`${source} -> ${threadId}:`, value);
channel.postMessage('done');
channel.close();
});
await postMessageToThread(0, { message: 'ping' });
}
channel.onmessage = channel.close;
const {
postMessageToThread,
threadId,
workerData,
Worker,
} = require('node:worker_threads');
const channel = new BroadcastChannel('sync');
const level = workerData?.level ?? 0;
if (level < 10) {
const worker = new Worker(__filename, {
workerData: { level: level + 1 },
});
}
if (level === 0) {
process.on('workerMessage', (value, source) => {
console.log(`${source} -> ${threadId}:`, value);
postMessageToThread(source, { message: 'pong' });
});
} else if (level === 10) {
process.on('workerMessage', (value, source) => {
console.log(`${source} -> ${threadId}:`, value);
channel.postMessage('done');
channel.close();
});
postMessageToThread(0, { message: 'ping' });
}
channel.onmessage = channel.close;
worker.receiveMessageOnPort(port)
[Cronologia]
Versione | Modifiche |
---|---|
v15.12.0 | L'argomento port può anche fare riferimento ora a un BroadcastChannel . |
v12.3.0 | Aggiunto in: v12.3.0 |
port
<MessagePort> | <BroadcastChannel>- Restituisce: <Object> | <undefined>
Riceve un singolo messaggio da una data MessagePort
. Se nessun messaggio è disponibile, viene restituito undefined
, altrimenti un oggetto con una singola proprietà message
che contiene il payload del messaggio, corrispondente al messaggio più vecchio nella coda della MessagePort
.
const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
port1.postMessage({ hello: 'world' });
console.log(receiveMessageOnPort(port2));
// Stampa: { message: { hello: 'world' } }
console.log(receiveMessageOnPort(port2));
// Stampa: undefined
Quando viene utilizzata questa funzione, nessun evento 'message'
viene emesso e il listener onmessage
non viene invocato.
worker.resourceLimits
Aggiunto in: v13.2.0, v12.16.0
Fornisce l'insieme di vincoli delle risorse del motore JS all'interno di questo thread Worker. Se l'opzione resourceLimits
è stata passata al costruttore Worker
, questo corrisponde ai suoi valori.
Se questo viene utilizzato nel thread principale, il suo valore è un oggetto vuoto.
worker.SHARE_ENV
Aggiunto in: v11.14.0
Un valore speciale che può essere passato come opzione env
del costruttore Worker
, per indicare che il thread corrente e il thread Worker devono condividere l'accesso in lettura e scrittura allo stesso insieme di variabili d'ambiente.
const { Worker, SHARE_ENV } = require('node:worker_threads');
new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV })
.on('exit', () => {
console.log(process.env.SET_IN_WORKER); // Stampa 'foo'.
});
worker.setEnvironmentData(key[, value])
[Cronologia]
Versione | Modifiche |
---|---|
v17.5.0, v16.15.0 | Non più sperimentale. |
v15.12.0, v14.18.0 | Aggiunto in: v15.12.0, v14.18.0 |
key
<any> Qualsiasi valore JavaScript arbitrario e clonabile che può essere utilizzato come chiave <Map>.value
<any> Qualsiasi valore JavaScript arbitrario e clonabile che verrà clonato e passato automaticamente a tutte le nuove istanzeWorker
. Sevalue
viene passato comeundefined
, qualsiasi valore impostato precedentemente per lakey
verrà eliminato.
L'API worker.setEnvironmentData()
imposta il contenuto di worker.getEnvironmentData()
nel thread corrente e in tutte le nuove istanze Worker
generate dal contesto corrente.
worker.threadId
Aggiunto in: v10.5.0
Un identificatore intero per il thread corrente. Sull'oggetto worker corrispondente (se presente), è disponibile come worker.threadId
. Questo valore è univoco per ciascuna istanza Worker
all'interno di un singolo processo.
worker.workerData
Aggiunto in: v10.5.0
Un valore JavaScript arbitrario che contiene un clone dei dati passati al costruttore Worker
di questo thread.
I dati vengono clonati come se si utilizzasse postMessage()
, secondo l'algoritmo di clonazione strutturata HTML.
const { Worker, isMainThread, workerData } = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename, { workerData: 'Hello, world!' });
} else {
console.log(workerData); // Stampa 'Hello, world!'.
}
Classe: BroadcastChannel extends EventTarget
[Cronologia]
Versione | Modifiche |
---|---|
v18.0.0 | Non più sperimentale. |
v15.4.0 | Aggiunto in: v15.4.0 |
Le istanze di BroadcastChannel
consentono la comunicazione asincrona uno-a-molti con tutte le altre istanze di BroadcastChannel
associate allo stesso nome di canale.
'use strict';
const {
isMainThread,
BroadcastChannel,
Worker,
} = require('node:worker_threads');
const bc = new BroadcastChannel('hello');
if (isMainThread) {
let c = 0;
bc.onmessage = (event) => {
console.log(event.data);
if (++c === 10) bc.close();
};
for (let n = 0; n < 10; n++)
new Worker(__filename);
} else {
bc.postMessage('hello from every worker');
bc.close();
}
new BroadcastChannel(name)
Aggiunto in: v15.4.0
name
<any> Il nome del canale a cui connettersi. È consentito qualsiasi valore JavaScript che può essere convertito in una stringa usando${name}
.
broadcastChannel.close()
Aggiunto in: v15.4.0
Chiude la connessione BroadcastChannel
.
broadcastChannel.onmessage
Aggiunto in: v15.4.0
- Tipo: <Function> Invocato con un singolo argomento
MessageEvent
quando viene ricevuto un messaggio.
broadcastChannel.onmessageerror
Aggiunto in: v15.4.0
- Tipo: <Funzione> Invocata quando un messaggio ricevuto non può essere deserializzato.
broadcastChannel.postMessage(message)
Aggiunto in: v15.4.0
message
<any> Qualsiasi valore JavaScript clonabile.
broadcastChannel.ref()
Aggiunto in: v15.4.0
Opposto di unref()
. Chiamare ref()
su un BroadcastChannel precedentemente unref()
ed non permette al programma di terminare se è l'unico handle attivo rimasto (il comportamento predefinito). Se la porta è ref()
ed, chiamare ref()
di nuovo non ha alcun effetto.
broadcastChannel.unref()
Aggiunto in: v15.4.0
Chiamare unref()
su un BroadcastChannel permette al thread di terminare se questo è l'unico handle attivo nel sistema di eventi. Se il BroadcastChannel è già unref()
ed, chiamare unref()
di nuovo non ha alcun effetto.
Classe: MessageChannel
Aggiunto in: v10.5.0
Le istanze della classe worker.MessageChannel
rappresentano un canale di comunicazione asincrono a due vie. MessageChannel
non ha metodi propri. new MessageChannel()
restituisce un oggetto con proprietà port1
e port2
, che si riferiscono a istanze MessagePort
collegate.
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log('ricevuto', message));
port2.postMessage({ foo: 'bar' });
// Stampa: ricevuto { foo: 'bar' } dall'ascoltatore `port1.on('message')`
Classe: MessagePort
[Cronologia]
Versione | Modifiche |
---|---|
v14.7.0 | Questa classe ora eredita da EventTarget anziché da EventEmitter . |
v10.5.0 | Aggiunto in: v10.5.0 |
- Estende: <EventTarget>
Le istanze della classe worker.MessagePort
rappresentano un'estremità di un canale di comunicazione asincrono a due vie. Può essere utilizzato per trasferire dati strutturati, regioni di memoria e altri MessagePort
tra diversi Worker
s.
Questa implementazione corrisponde a MessagePort
del browser.
Evento: 'close'
Aggiunto in: v10.5.0
L'evento 'close'
viene emesso quando uno dei due lati del canale è stato disconnesso.
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
// Stampa:
// foobar
// closed!
port2.on('message', (message) => console.log(message));
port2.on('close', () => console.log('closed!'));
port1.postMessage('foobar');
port1.close();
Evento: 'message'
Aggiunto in: v10.5.0
value
<any> Il valore trasmesso
L'evento 'message'
viene emesso per ogni messaggio in arrivo, contenente l'input clonato di port.postMessage()
.
I listener su questo evento ricevono un clone del parametro value
come passato a postMessage()
e nessun altro argomento.
Evento: 'messageerror'
Aggiunto in: v14.5.0, v12.19.0
error
<Error> Un oggetto Error
L'evento 'messageerror'
viene emesso quando la deserializzazione di un messaggio non è riuscita.
Attualmente, questo evento viene emesso quando si verifica un errore durante la creazione di un'istanza dell'oggetto JS inviato all'estremità ricevente. Tali situazioni sono rare, ma possono verificarsi, ad esempio, quando determinati oggetti API di Node.js vengono ricevuti in un vm.Context
(dove le API di Node.js non sono attualmente disponibili).
port.close()
Aggiunto in: v10.5.0
Disabilita l'ulteriore invio di messaggi su entrambi i lati della connessione. Questo metodo può essere chiamato quando non si verificherà un'ulteriore comunicazione su questa MessagePort
.
L'evento 'close'
viene emesso su entrambe le istanze MessagePort
che fanno parte del canale.
port.postMessage(value[, transferList])
[Cronologia]
Versione | Modifiche |
---|---|
v21.0.0 | Viene generato un errore quando un oggetto non trasferibile si trova nell'elenco di trasferimento. |
v15.6.0 | Aggiunto X509Certificate all'elenco dei tipi clonabili. |
v15.0.0 | Aggiunto CryptoKey all'elenco dei tipi clonabili. |
v15.14.0, v14.18.0 | Aggiunto 'BlockList' all'elenco dei tipi clonabili. |
v15.9.0, v14.18.0 | Aggiunto tipi 'Histogram' all'elenco dei tipi clonabili. |
v14.5.0, v12.19.0 | Aggiunto KeyObject all'elenco dei tipi clonabili. |
v14.5.0, v12.19.0 | Aggiunto FileHandle all'elenco dei tipi trasferibili. |
v10.5.0 | Aggiunto in: v10.5.0 |
value
<any>transferList
<Object[]>
Invia un valore JavaScript al lato ricevente di questo canale. value
viene trasferito in un modo compatibile con l'algoritmo di clonazione strutturata HTML.
In particolare, le differenze significative rispetto a JSON
sono:
value
può contenere riferimenti circolari.value
può contenere istanze di tipi JS integrati comeRegExp
,BigInt
,Map
,Set
, ecc.value
può contenere array tipizzati, utilizzando siaArrayBuffer
cheSharedArrayBuffer
.value
può contenere istanze diWebAssembly.Module
.value
non può contenere oggetti nativi (supportati da C++) diversi da:
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log(message));
const circularData = {};
circularData.foo = circularData;
// Prints: { foo: [Circular] }
port2.postMessage(circularData);
transferList
può essere un elenco di oggetti ArrayBuffer
, MessagePort
e FileHandle
. Dopo il trasferimento, non sono più utilizzabili sul lato mittente del canale (anche se non sono contenuti in value
). A differenza dei processi figlio, il trasferimento di handle come i socket di rete non è attualmente supportato.
Se value
contiene istanze di SharedArrayBuffer
, queste sono accessibili da entrambi i thread. Non possono essere elencati in transferList
.
value
può ancora contenere istanze di ArrayBuffer
che non sono in transferList
; in tal caso, la memoria sottostante viene copiata anziché spostata.
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log(message));
const uint8Array = new Uint8Array([ 1, 2, 3, 4 ]);
// Questo invia una copia di `uint8Array`:
port2.postMessage(uint8Array);
// Questo non copia i dati, ma rende `uint8Array` inutilizzabile:
port2.postMessage(uint8Array, [ uint8Array.buffer ]);
// La memoria per `sharedUint8Array` è accessibile sia dall'originale
// che dalla copia ricevuta da `.on('message')`:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
port2.postMessage(sharedUint8Array);
// Questo trasferisce una message port appena creata al ricevitore.
// Questo può essere utilizzato, ad esempio, per creare canali di comunicazione tra
// più thread `Worker` che sono figli dello stesso thread padre.
const otherChannel = new MessageChannel();
port2.postMessage({ port: otherChannel.port1 }, [ otherChannel.port1 ]);
L'oggetto messaggio viene clonato immediatamente e può essere modificato dopo la pubblicazione senza avere effetti collaterali.
Per ulteriori informazioni sui meccanismi di serializzazione e deserializzazione alla base di questa API, consulta l'API di serializzazione del modulo node:v8
.
Considerazioni durante il trasferimento di TypedArray e Buffer
Tutte le istanze TypedArray
e Buffer
sono viste su un ArrayBuffer
sottostante. Cioè, è l'ArrayBuffer
che effettivamente memorizza i dati grezzi mentre gli oggetti TypedArray
e Buffer
forniscono un modo per visualizzare e manipolare i dati. È possibile e comune creare più viste sulla stessa istanza ArrayBuffer
. È necessario prestare molta attenzione quando si utilizza un elenco di trasferimento per trasferire un ArrayBuffer
poiché ciò rende inutilizzabili tutte le istanze TypedArray
e Buffer
che condividono lo stesso ArrayBuffer
.
const ab = new ArrayBuffer(10);
const u1 = new Uint8Array(ab);
const u2 = new Uint16Array(ab);
console.log(u2.length); // stampa 5
port.postMessage(u1, [u1.buffer]);
console.log(u2.length); // stampa 0
Per le istanze Buffer
, in particolare, se l'ArrayBuffer
sottostante può essere trasferito o clonato dipende interamente da come sono state create le istanze, il che spesso non può essere determinato in modo affidabile.
Un ArrayBuffer
può essere contrassegnato con markAsUntransferable()
per indicare che dovrebbe sempre essere clonato e mai trasferito.
A seconda di come è stata creata un'istanza Buffer
, potrebbe o meno possedere il suo ArrayBuffer
sottostante. Un ArrayBuffer
non deve essere trasferito a meno che non sia noto che l'istanza Buffer
lo possiede. In particolare, per i Buffer
creati dal pool Buffer
interno (utilizzando, ad esempio, Buffer.from()
o Buffer.allocUnsafe()
), il loro trasferimento non è possibile e vengono sempre clonati, il che invia una copia dell'intero pool Buffer
. Questo comportamento può comportare un utilizzo di memoria più elevato non intenzionale e possibili problemi di sicurezza.
Vedere Buffer.allocUnsafe()
per maggiori dettagli sul pooling di Buffer
.
Gli ArrayBuffer
per le istanze Buffer
create utilizzando Buffer.alloc()
o Buffer.allocUnsafeSlow()
possono sempre essere trasferiti, ma ciò rende inutilizzabili tutte le altre viste esistenti di tali ArrayBuffer
.
Considerazioni durante la clonazione di oggetti con prototipi, classi e accessorie
Poiché la clonazione di oggetti utilizza l'algoritmo di clonazione strutturata HTML, le proprietà non enumerabili, le accessorie di proprietà e i prototipi di oggetti non vengono preservati. In particolare, gli oggetti Buffer
verranno letti come semplici Uint8Array
sul lato ricevente e le istanze di classi JavaScript verranno clonate come semplici oggetti JavaScript.
const b = Symbol('b');
class Foo {
#a = 1;
constructor() {
this[b] = 2;
this.c = 3;
}
get d() { return 4; }
}
const { port1, port2 } = new MessageChannel();
port1.onmessage = ({ data }) => console.log(data);
port2.postMessage(new Foo());
// Prints: { c: 3 }
Questa limitazione si estende a molti oggetti integrati, come l'oggetto globale URL
:
const { port1, port2 } = new MessageChannel();
port1.onmessage = ({ data }) => console.log(data);
port2.postMessage(new URL('https://example.org'));
// Prints: { }
port.hasRef()
Aggiunto in: v18.1.0, v16.17.0
[Stabile: 1 - Sperimentale]
Stabile: 1 Stabilità: 1 - Sperimentale
- Restituisce: <boolean>
Se vero, l'oggetto MessagePort
manterrà attivo il ciclo di eventi di Node.js.
port.ref()
Aggiunto in: v10.5.0
Opposto di unref()
. Chiamare ref()
su una porta precedentemente unref()
ed non consente al programma di uscire se è l'unico handle attivo rimasto (il comportamento predefinito). Se la porta è ref()
ed, chiamare di nuovo ref()
non ha alcun effetto.
Se i listener vengono allegati o rimossi utilizzando .on('message')
, la porta viene ref()
ed e unref()
ed automaticamente a seconda che esistano listener per l'evento.
port.start()
Aggiunto in: v10.5.0
Inizia a ricevere messaggi su questa MessagePort
. Quando si utilizza questa porta come un emettitore di eventi, questo metodo viene chiamato automaticamente una volta che i listener 'message'
sono collegati.
Questo metodo esiste per parità con l'API Web MessagePort
. In Node.js, è utile solo per ignorare i messaggi quando non è presente alcun listener di eventi. Node.js diverge anche nella sua gestione di .onmessage
. Impostandolo chiama automaticamente .start()
, ma disattivandolo permette ai messaggi di mettersi in coda fino a quando non viene impostato un nuovo gestore o la porta viene scartata.
port.unref()
Aggiunto in: v10.5.0
Chiamare unref()
su una porta permette al thread di terminare se questo è l'unico handle attivo nel sistema di eventi. Se la porta è già unref()
ed chiamare unref()
di nuovo non ha alcun effetto.
Se i listener sono collegati o rimossi usando .on('message')
, la porta è ref()
ed e unref()
ed automaticamente a seconda che i listener per l'evento esistano.
Classe: Worker
Aggiunto in: v10.5.0
- Estende: <EventEmitter>
La classe Worker
rappresenta un thread di esecuzione JavaScript indipendente. La maggior parte delle API di Node.js sono disponibili al suo interno.
Le differenze notevoli all'interno di un ambiente Worker sono:
- Gli stream
process.stdin
,process.stdout
eprocess.stderr
possono essere reindirizzati dal thread padre. - La proprietà
require('node:worker_threads').isMainThread
è impostata sufalse
. - La porta dei messaggi
require('node:worker_threads').parentPort
è disponibile. process.exit()
non ferma l'intero programma, solo il singolo thread, eprocess.abort()
non è disponibile.process.chdir()
e i metodiprocess
che impostano ID di gruppo o utente non sono disponibili.process.env
è una copia delle variabili d'ambiente del thread padre, a meno che non sia specificato diversamente. Le modifiche a una copia non sono visibili in altri thread e non sono visibili ai componenti aggiuntivi nativi (a meno cheworker.SHARE_ENV
non venga passato come opzioneenv
al costruttoreWorker
). Su Windows, a differenza del thread principale, una copia delle variabili d'ambiente opera in modo case-sensitive.process.title
non può essere modificato.- I segnali non vengono inviati tramite
process.on('...')
. - L'esecuzione può fermarsi in qualsiasi momento a seguito dell'invocazione di
worker.terminate()
. - I canali IPC dai processi padre non sono accessibili.
- Il modulo
trace_events
non è supportato. - I componenti aggiuntivi nativi possono essere caricati da più thread solo se soddisfano determinate condizioni.
È possibile creare istanze Worker
all'interno di altri Worker
.
Come i Web Worker e il modulo node:cluster
, la comunicazione bidirezionale può essere ottenuta tramite il passaggio di messaggi tra thread. Internamente, un Worker
ha una coppia incorporata di MessagePort
che sono già associate l'una all'altra quando viene creato il Worker
. Mentre l'oggetto MessagePort
sul lato padre non è direttamente esposto, le sue funzionalità sono esposte tramite worker.postMessage()
e l'evento worker.on('message')
sull'oggetto Worker
per il thread padre.
Per creare canali di messaggistica personalizzati (che è incoraggiato rispetto all'utilizzo del canale globale predefinito perché facilita la separazione delle preoccupazioni), gli utenti possono creare un oggetto MessageChannel
su uno dei due thread e passare una delle MessagePort
su quel MessageChannel
all'altro thread attraverso un canale preesistente, come quello globale.
Vedere port.postMessage()
per maggiori informazioni su come i messaggi vengono passati e che tipo di valori JavaScript possono essere trasportati con successo attraverso la barriera del thread.
const assert = require('node:assert');
const {
Worker, MessageChannel, MessagePort, isMainThread, parentPort,
} = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
const subChannel = new MessageChannel();
worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]);
subChannel.port2.on('message', (value) => {
console.log('received:', value);
});
} else {
parentPort.once('message', (value) => {
assert(value.hereIsYourPort instanceof MessagePort);
value.hereIsYourPort.postMessage('the worker is sending this');
value.hereIsYourPort.close();
});
}
new Worker(filename[, options])
[Cronologia]
Versione | Modifiche |
---|---|
v19.8.0, v18.16.0 | Aggiunto il supporto per un'opzione name , che consente di aggiungere un nome al titolo del worker per il debug. |
v14.9.0 | Il parametro filename può essere un oggetto URL WHATWG che utilizza il protocollo data: . |
v14.9.0 | L'opzione trackUnmanagedFds è stata impostata su true per impostazione predefinita. |
v14.6.0, v12.19.0 | È stata introdotta l'opzione trackUnmanagedFds . |
v13.13.0, v12.17.0 | È stata introdotta l'opzione transferList . |
v13.12.0, v12.17.0 | Il parametro filename può essere un oggetto URL WHATWG che utilizza il protocollo file: . |
v13.4.0, v12.16.0 | È stata introdotta l'opzione argv . |
v13.2.0, v12.16.0 | È stata introdotta l'opzione resourceLimits . |
v10.5.0 | Aggiunto in: v10.5.0 |
filename
<stringa> | <URL> Il percorso dello script principale o del modulo del Worker. Deve essere un percorso assoluto o un percorso relativo (ovvero, relativo alla directory di lavoro corrente) che inizia con./
o../
, oppure un oggettoURL
WHATWG che utilizza il protocollofile:
odata:
. Quando si utilizza undata:
URL, i dati vengono interpretati in base al tipo MIME utilizzando il caricatore di moduli ECMAScript. Seoptions.eval
ètrue
, questo è una stringa contenente codice JavaScript anziché un percorso.options
<Oggetto>argv
<any[]> Elenco di argomenti che verrebbero convertiti in stringa e aggiunti aprocess.argv
nel worker. Questo è per lo più simile aworkerData
, ma i valori sono disponibili suprocess.argv
globale come se fossero stati passati come opzioni CLI allo script.env
<Oggetto> Se impostato, specifica il valore iniziale diprocess.env
all'interno del thread Worker. Come valore speciale, è possibile utilizzareworker.SHARE_ENV
per specificare che il thread padre e il thread figlio devono condividere le loro variabili di ambiente; in tal caso, le modifiche all'oggettoprocess.env
di un thread influiscono anche sull'altro thread. Predefinito:process.env
.eval
<boolean> Setrue
e il primo argomento è unastringa
, interpreta il primo argomento del costruttore come uno script che viene eseguito una volta che il worker è online.execArgv
<string[]> Elenco delle opzioni CLI di node passate al worker. Le opzioni V8 (come--max-old-space-size
) e le opzioni che influiscono sul processo (come--title
) non sono supportate. Se impostato, questo viene fornito comeprocess.execArgv
all'interno del worker. Per impostazione predefinita, le opzioni vengono ereditate dal thread padre.stdin
<boolean> Se questo è impostato sutrue
, alloraworker.stdin
fornisce un flusso scrivibile il cui contenuto appare comeprocess.stdin
all'interno del Worker. Per impostazione predefinita, non vengono forniti dati.stdout
<boolean> Se questo è impostato sutrue
, alloraworker.stdout
non viene automaticamente inviato tramite pipe aprocess.stdout
nel padre.stderr
<boolean> Se questo è impostato sutrue
, alloraworker.stderr
non viene automaticamente inviato tramite pipe aprocess.stderr
nel padre.workerData
<any> Qualsiasi valore JavaScript che viene clonato e reso disponibile comerequire('node:worker_threads').workerData
. La clonazione avviene come descritto nell'algoritmo di clonazione strutturata HTML, e viene generato un errore se l'oggetto non può essere clonato (ad esempio, perché contienefunction
).trackUnmanagedFds
<boolean> Se questo è impostato sutrue
, allora il Worker tiene traccia dei descrittori di file raw gestiti tramitefs.open()
efs.close()
, e li chiude quando il Worker esce, in modo simile ad altre risorse come i socket di rete o i descrittori di file gestiti tramite l'APIFileHandle
. Questa opzione viene ereditata automaticamente da tutti iWorker
nidificati. Predefinito:true
.transferList
<Object[]> Se uno o più oggetti simili aMessagePort
vengono passati inworkerData
, è richiesto untransferList
per quegli elementi oppure viene generatoERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
. Vediport.postMessage()
per maggiori informazioni.resourceLimits
<Oggetto> Un insieme opzionale di limiti di risorse per la nuova istanza del motore JS. Il raggiungimento di questi limiti porta alla terminazione dell'istanzaWorker
. Questi limiti influiscono solo sul motore JS e su nessun dato esterno, inclusi gliArrayBuffer
. Anche se questi limiti sono impostati, il processo potrebbe comunque interrompersi se si verifica una situazione globale di esaurimento della memoria.maxOldGenerationSizeMb
<numero> La dimensione massima dell'heap principale in MB. Se l'argomento della riga di comando--max-old-space-size
è impostato, sovrascrive questa impostazione.maxYoungGenerationSizeMb
<numero> La dimensione massima di uno spazio heap per oggetti creati di recente. Se l'argomento della riga di comando--max-semi-space-size
è impostato, sovrascrive questa impostazione.codeRangeSizeMb
<numero> La dimensione di un intervallo di memoria pre-allocato utilizzato per il codice generato.stackSizeMb
<numero> La dimensione massima predefinita dello stack per il thread. Valori piccoli possono portare a istanze Worker inutilizzabili. Predefinito:4
.name
<stringa> Unname
opzionale da aggiungere al titolo del worker per scopi di debug/identificazione, rendendo il titolo finale come[worker ${id}] ${name}
. Predefinito:''
.
Evento: 'error'
Aggiunto in: v10.5.0
err
<Error>
L'evento 'error'
viene emesso se il thread worker genera un'eccezione non catturata. In tal caso, il worker viene terminato.
Evento: 'exit'
Aggiunto in: v10.5.0
exitCode
<integer>
L'evento 'exit'
viene emesso una volta che il worker si è fermato. Se il worker è uscito chiamando process.exit()
, il parametro exitCode
è il codice di uscita passato. Se il worker è stato terminato, il parametro exitCode
è 1
.
Questo è l'evento finale emesso da qualsiasi istanza Worker
.
Evento: 'message'
Aggiunto in: v10.5.0
value
<any> Il valore trasmesso
L'evento 'message'
viene emesso quando il thread worker ha invocato require('node:worker_threads').parentPort.postMessage()
. Vedi l'evento port.on('message')
per maggiori dettagli.
Tutti i messaggi inviati dal thread worker vengono emessi prima che l''exit'
event venga emesso sull'oggetto Worker
.
Evento: 'messageerror'
Aggiunto in: v14.5.0, v12.19.0
error
<Error> Un oggetto Error
L'evento 'messageerror'
viene emesso quando la deserializzazione di un messaggio non è andata a buon fine.
Evento: 'online'
Aggiunto in: v10.5.0
L'evento 'online'
viene emesso quando il thread worker ha iniziato a eseguire codice JavaScript.
worker.getHeapSnapshot([options])
[Cronologia]
Versione | Modifiche |
---|---|
v19.1.0 | Supporto delle opzioni per configurare lo snapshot dell'heap. |
v13.9.0, v12.17.0 | Aggiunto in: v13.9.0, v12.17.0 |
options
<Object>Restituisce: <Promise> Una promise per uno Stream leggibile contenente uno snapshot dell'heap V8
Restituisce uno stream leggibile per uno snapshot V8 dello stato corrente del Worker. Vedere v8.getHeapSnapshot()
per maggiori dettagli.
Se il thread Worker non è più in esecuzione, il che può verificarsi prima che l'evento 'exit'
event venga emesso, la Promise
restituita viene rifiutata immediatamente con un errore ERR_WORKER_NOT_RUNNING
.
worker.performance
Aggiunto in: v15.1.0, v14.17.0, v12.22.0
Un oggetto che può essere utilizzato per interrogare le informazioni sulle prestazioni da un'istanza di worker. Simile a perf_hooks.performance
.
performance.eventLoopUtilization([utilization1[, utilization2]])
Aggiunto in: v15.1.0, v14.17.0, v12.22.0
utilization1
<Object> Il risultato di una precedente chiamata aeventLoopUtilization()
.utilization2
<Object> Il risultato di una precedente chiamata aeventLoopUtilization()
prima diutilization1
.- Restituisce: <Object>
La stessa chiamata di perf_hooks
eventLoopUtilization()
, tranne per il fatto che vengono restituiti i valori dell'istanza del worker.
Una differenza è che, a differenza del thread principale, il bootstrapping all'interno di un worker viene eseguito all'interno del ciclo di eventi. Quindi l'utilizzo del ciclo di eventi è immediatamente disponibile una volta che lo script del worker inizia l'esecuzione.
Un tempo idle
che non aumenta non indica che il worker è bloccato nel bootstrap. I seguenti esempi mostrano come l'intera durata del worker non accumuli mai alcun tempo idle
, ma sia comunque in grado di elaborare i messaggi.
const { Worker, isMainThread, parentPort } = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
setInterval(() => {
worker.postMessage('hi');
console.log(worker.performance.eventLoopUtilization());
}, 100).unref();
return;
}
parentPort.on('message', () => console.log('msg')).unref();
(function r(n) {
if (--n < 0) return;
const t = Date.now();
while (Date.now() - t < 300);
setImmediate(r, n);
})(10);
L'utilizzo del ciclo di eventi di un worker è disponibile solo dopo l'emissione dell'evento 'online'
event e, se chiamato prima di questo, o dopo l'evento 'exit'
event, tutte le proprietà hanno il valore di 0
.
worker.postMessage(value[, transferList])
Aggiunto in: v10.5.0
value
<any>transferList
<Object[]>
Invia un messaggio al worker che viene ricevuto tramite require('node:worker_threads').parentPort.on('message')
. Vedi port.postMessage()
per maggiori dettagli.
worker.ref()
Aggiunto in: v10.5.0
L'opposto di unref()
, chiamare ref()
su un worker precedentemente unref()
ed non permette al programma di uscire se è l'unico handle attivo rimasto (il comportamento predefinito). Se il worker è ref()
ed, chiamare ref()
di nuovo non ha alcun effetto.
worker.resourceLimits
Aggiunto in: v13.2.0, v12.16.0
Fornisce l'insieme dei vincoli delle risorse del motore JS per questo thread Worker. Se l'opzione resourceLimits
è stata passata al costruttore Worker
, questo corrisponde ai suoi valori.
Se il worker si è fermato, il valore di ritorno è un oggetto vuoto.
worker.stderr
Aggiunto in: v10.5.0
Questo è un flusso leggibile che contiene i dati scritti in process.stderr
all'interno del thread worker. Se stderr: true
non è stato passato al costruttore Worker
, allora i dati vengono indirizzati al flusso process.stderr
del thread principale.
worker.stdin
Aggiunto in: v10.5.0
Se è stato passato stdin: true
al costruttore Worker
, questo è un flusso scrivibile. I dati scritti in questo flusso saranno resi disponibili nel thread worker come process.stdin
.
worker.stdout
Aggiunto in: v10.5.0
Questo è un flusso leggibile che contiene i dati scritti in process.stdout
all'interno del thread worker. Se stdout: true
non è stato passato al costruttore Worker
, allora i dati vengono indirizzati al flusso process.stdout
del thread padre.
worker.terminate()
[Cronologia]
Versione | Modifiche |
---|---|
v12.5.0 | Questa funzione ora restituisce una Promise. Passare un callback è deprecato ed è stato inutile fino a questa versione, poiché il Worker veniva effettivamente terminato in modo sincrono. La terminazione è ora un'operazione completamente asincrona. |
v10.5.0 | Aggiunto in: v10.5.0 |
- Restituisce: <Promise>
Interrompe l'esecuzione di tutti i JavaScript nel thread worker il prima possibile. Restituisce una Promise per il codice di uscita che viene soddisfatta quando viene emesso l' 'exit'
event.
worker.threadId
Aggiunto in: v10.5.0
Un identificatore intero per il thread referenziato. All'interno del thread worker, è disponibile come require('node:worker_threads').threadId
. Questo valore è univoco per ogni istanza Worker
all'interno di un singolo processo.
worker.unref()
Aggiunto in: v10.5.0
Chiamare unref()
su un worker consente al thread di uscire se questo è l'unico handle attivo nel sistema di eventi. Se il worker è già unref()
ed chiamare unref()
di nuovo non ha effetto.
Note
Blocco sincrono di stdio
I Worker
utilizzano il passaggio di messaggi tramite <MessagePort> per implementare le interazioni con stdio
. Ciò significa che l'output stdio
proveniente da un Worker
può essere bloccato da codice sincrono all'estremità ricevente che sta bloccando l'event loop di Node.js.
import {
Worker,
isMainThread,
} from 'node:worker_threads';
if (isMainThread) {
new Worker(new URL(import.meta.url));
for (let n = 0; n < 1e10; n++) {
// Ciclo per simulare il lavoro.
}
} else {
// Questo output sarà bloccato dal ciclo for nel thread principale.
console.log('foo');
}
'use strict';
const {
Worker,
isMainThread,
} = require('node:worker_threads');
if (isMainThread) {
new Worker(__filename);
for (let n = 0; n < 1e10; n++) {
// Ciclo per simulare il lavoro.
}
} else {
// Questo output sarà bloccato dal ciclo for nel thread principale.
console.log('foo');
}
Avvio di thread worker da script di precaricamento
Prestare attenzione quando si avviano thread worker da script di precaricamento (script caricati ed eseguiti utilizzando il flag della riga di comando -r
). A meno che l'opzione execArgv
non sia impostata esplicitamente, i nuovi thread Worker ereditano automaticamente i flag della riga di comando dal processo in esecuzione e precaricheranno gli stessi script di precaricamento del thread principale. Se lo script di precaricamento avvia incondizionatamente un thread worker, ogni thread generato ne genererà un altro fino a quando l'applicazione non si arresta in modo anomalo.