Thread dei worker
[Stabile: 2 - Stabile]
Stabile: 2 Stabilità: 2 - Stabile
Codice sorgente: lib/worker_threads.js
Il modulo node:worker_threads
consente l'utilizzo di thread che eseguono JavaScript in parallelo. Per accedervi:
const worker = require('node:worker_threads')
I worker (thread) sono utili per eseguire operazioni JavaScript intensive dal punto di vista della CPU. Non sono di grande aiuto per il lavoro intensivo di I/O. Le operazioni di I/O asincrone integrate di Node.js sono più efficienti di quanto possano essere i Worker.
A differenza di child_process
o cluster
, worker_threads
può condividere la memoria. Lo fa 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 interrotto con codice di uscita ${code}`))
})
})
}
} else {
const { parse } = require('some-js-parsing-library')
const script = workerData
parentPort.postMessage(parse(script))
}
L'esempio sopra genera un thread Worker per ogni chiamata parseJSAsync()
. In pratica, utilizzare un pool di Worker per questo tipo di attività. Altrimenti, il sovraccarico di creazione dei Worker probabilmente supererebbe i loro vantaggi.
Quando si implementa un pool di worker, utilizzare l'API AsyncResource
per informare gli strumenti di diagnostica (ad esempio, per fornire tracce di stack asincrone) sulla correlazione tra le attività e i loro risultati. Vedere "Utilizzo di AsyncResource
per un pool di thread Worker
" nella documentazione di async_hooks
per un esempio di implementazione.
I thread dei worker ereditano le opzioni non specifiche del processo per impostazione predefinita. Fare riferimento a Opzioni del costruttore Worker
per sapere come personalizzare le opzioni del thread dei worker, 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 usato come chiave di una <Map>.- Restituisce: <any>
All'interno di un thread worker, worker.getEnvironmentData()
restituisce un clone dei dati passati al thread di generazione worker.setEnvironmentData()
. Ogni nuovo Worker
riceve automaticamente la sua copia dei dati dell'ambiente.
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('Dentro 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 trova nell'elenco di trasferimento di una chiamata port.postMessage()
, viene generato un errore. Questa è un'operazione senza effetto 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 di invio. Ad esempio, Node.js contrassegna gli ArrayBuffer
che utilizza per il suo pool Buffer
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 avrebbe avuto successo.
// typedArray2 è intatto anche.
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 di 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 port.postMessage()
, viene generato un errore. Questa è un'operazione senza effetto se object
è un valore primitivo.
Questo non ha 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 di 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 contextified come restituito dal metodovm.createContext()
.- Restituisce: <MessagePort>
Trasferisce un MessagePort
a un contesto vm
diverso. L'oggetto port
originale viene reso inutilizzabile e l'istanza MessagePort
restituita prende il suo posto.
Il MessagePort
restituito è un oggetto nel contesto di destinazione ed eredita dalla sua classe globale Object
. Anche gli oggetti passati all'ascoltatore port.onmessage()
vengono creati nel contesto di destinazione ed ereditano dalla sua classe globale Object
.
Tuttavia, il MessagePort
creato non eredita più da EventTarget
, e solo port.onmessage()
può essere utilizzato per ricevere eventi usandolo.
worker.parentPort
Aggiunto in: v10.5.0
Se questo thread è un Worker
, questo è un MessagePort
che permette la comunicazione con il thread principale. I messaggi inviati usando parentPort.postMessage()
sono disponibili nel thread principale usando worker.on('message')
, e i messaggi inviati dal thread principale 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 principale, rimandalo 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à sollevato un erroreERR_WORKER_MESSAGING_FAILED
. Se l'ID del thread di destinazione è l'ID del thread corrente, verrà sollevato un erroreERR_WORKER_MESSAGING_SAME_THREAD
.value
<any> Il valore da inviare.transferList
<Object[]> Se uno o più oggetti simili aMessagePort
vengono passati invalue
, è richiesto untransferList
per tali elementi oppure viene sollevatoERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
. Vediport.postMessage()
per maggiori informazioni.timeout
<number> Tempo di attesa per la consegna del messaggio in millisecondi. Per impostazione predefinita èundefined
, il che significa attendere per sempre. Se l'operazione si interrompe per timeout, viene sollevato un erroreERR_WORKER_MESSAGING_TIMEOUT
.- Restituisce: <Promise> Una promessa che viene mantenuta 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 un listener per l'evento workerMessage
, l'operazione solleverà un errore ERR_WORKER_MESSAGING_FAILED
.
Se il thread di destinazione ha sollevato un errore durante l'elaborazione dell'evento workerMessage
, l'operazione solleverà un errore ERR_WORKER_MESSAGING_ERRORED
.
Questo metodo dovrebbe essere usato quando il thread di destinazione non è il padre o il figlio diretto del thread corrente. Se i due thread sono padre-figlio, usa require('node:worker_threads').parentPort.postMessage()
e worker.postMessage()
per permettere ai thread di comunicare.
L'esempio seguente mostra l'utilizzo di postMessageToThread
: crea 10 thread annidati, l'ultimo cercherà 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 ora può anche riferirsi a un BroadcastChannel . |
v12.3.0 | Aggiunto in: v12.3.0 |
port
<MessagePort> | <BroadcastChannel>- Restituisce: <Object> | <undefined>
Riceve un singolo messaggio da un dato 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 di 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 questa funzione viene utilizzata, 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 dei vincoli di 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 dovrebbero condividere l'accesso in lettura e scrittura allo stesso insieme di variabili di 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
è passato comeundefined
, qualsiasi valore precedentemente impostato perkey
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 identificativo intero per il thread corrente. Sull'oggetto worker corrispondente (se presente), è disponibile come worker.threadId
. Questo valore è univoco per ogni 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 stesse usando 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 del 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> Viene invocato con un singolo argomento
MessageEvent
quando si riceve un messaggio.
broadcastChannel.onmessageerror
Aggiunto in: v15.4.0
- Tipo: <Function> Viene invocato 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 uscire se è l'unico handle attivo rimanente (comportamento predefinito). Se la porta è ref()
ed, chiamare ref()
di nuovo non ha effetto.
broadcastChannel.unref()
Aggiunto in: v15.4.0
Chiamare unref()
su un BroadcastChannel permette al thread di uscire se questo è l'unico handle attivo nel sistema di eventi. Se il BroadcastChannel è già unref()
ed chiamare unref()
di nuovo non ha effetto.
Classe: MessageChannel
Aggiunto in: v10.5.0
Le istanze della classe worker.MessageChannel
rappresentano un canale di comunicazione bidirezionale asincrono. MessageChannel
non ha metodi propri. new MessageChannel()
restituisce un oggetto con le proprietà port1
e port2
, che si riferiscono a istanze collegate di MessagePort
.
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' } dal listener `port1.on('message')`
Classe: MessagePort
[Cronologia]
Versione | Modifiche |
---|---|
v14.7.0 | Questa classe ora eredita da EventTarget invece che da EventEmitter . |
v10.5.0 | Aggiunto in: v10.5.0 |
- Estensioni: <EventTarget>
Le istanze della classe worker.MessagePort
rappresentano un'estremità di un canale di comunicazione bidirezionale asincrono. Può essere utilizzato per trasferire dati strutturati, regioni di memoria e altri MessagePort
tra diversi Worker
.
Questa implementazione corrisponde ai browser MessagePort
.
Evento: 'close'
Aggiunto in: v10.5.0
L'evento 'close'
viene emesso una volta che 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()
.
Gli ascoltatori di 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 fallisce.
Attualmente, questo evento viene emesso quando si verifica un errore durante l'istanza dell'oggetto JS pubblicato sul lato ricevente. Tali situazioni sono rare, ma possono accadere, ad esempio, quando vengono ricevuti determinati oggetti API di Node.js 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 avverrà più alcuna comunicazione su questo 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 è presente 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 | Aggiunti 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 predefiniti comeRegExp
,BigInt
,Map
,Set
, ecc.value
può contenere array tipizzati, sia usandoArrayBuffer
cheSharedArrayBuffer
.value
può contenere istanzeWebAssembly.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
// Stampa: { 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 di invio del canale (anche se non sono contenuti in value
). A differenza dei processi figlio, il trasferimento di handle come socket di rete non è attualmente supportato.
Se value
contiene istanze SharedArrayBuffer
, queste sono accessibili da entrambi i thread. Non possono essere elencate in transferList
.
value
può comunque contenere istanze 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 pubblica 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 porta del messaggio 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 principale.
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 dietro questa API, consultare l'API di serializzazione del modulo node:v8
.
Considerazioni sul trasferimento di TypedArray e Buffer
Tutte le istanze di TypedArray
e Buffer
sono viste di un ArrayBuffer
sottostante. Cioè, è l' ArrayBuffer
che memorizza effettivamente i dati grezzi, mentre gli oggetti TypedArray
e Buffer
forniscono un modo per visualizzare e manipolare i dati. È possibile e comune creare più viste sullo stesso istanza di ArrayBuffer
. È necessario prestare molta attenzione quando si utilizza un elenco di trasferimento per trasferire un ArrayBuffer
, poiché ciò rende inutilizzabili tutte le istanze di 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 di 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 deve sempre essere clonato e mai trasferito.
A seconda di come è stata creata un'istanza di 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 possieda. In particolare, per i Buffer
creati dal pool interno Buffer
(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 della memoria più elevato e non intenzionale e possibili problemi di sicurezza.
Vedi Buffer.allocUnsafe()
per maggiori dettagli sul pooling di Buffer
.
Gli ArrayBuffer
per le istanze di Buffer
create utilizzando Buffer.alloc()
o Buffer.allocUnsafeSlow()
possono sempre essere trasferiti, ma farlo rende inutilizzabili tutte le altre viste esistenti di tali ArrayBuffer
.
Considerazioni sulla clonazione di oggetti con prototipi, classi e accessor
Poiché la clonazione di oggetti utilizza l' algoritmo di clonazione strutturata HTML, le proprietà non enumerabili, gli accessor delle proprietà e i prototipi degli oggetti non vengono preservati. In particolare, gli oggetti Buffer
verranno letti come semplici Uint8Array
sul lato ricevente e le istanze delle 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())
// Stampa: { c: 3 }
Questa limitazione si estende a molti oggetti built-in, come l'oggetto globale URL
:
const { port1, port2 } = new MessageChannel()
port1.onmessage = ({ data }) => console.log(data)
port2.postMessage(new URL('https://example.org'))
// Stampa: { }
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 loop degli 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'unica handle attiva rimasta (il comportamento predefinito). Se la porta è ref()
ed, chiamare nuovamente ref()
non ha effetto.
Se gli ascoltatori vengono aggiunti o rimossi usando .on('message')
, la porta viene ref()
ed e unref()
ed automaticamente a seconda che esistano ascoltatori per l'evento.
port.start()
Aggiunto in: v10.5.0
Inizia a ricevere messaggi su questo MessagePort
. Quando si utilizza questa porta come emittente di eventi, questa viene chiamata automaticamente una volta che vengono collegati gli ascoltatori 'message'
.
Questo metodo esiste per parità con l'API MessagePort
del Web. In Node.js, è utile solo per ignorare i messaggi quando non è presente alcun ascoltatore di eventi. Node.js diverge anche nella gestione di .onmessage
. Impostandolo chiama automaticamente .start()
, ma la sua disattivazione consente ai messaggi di mettersi in coda fino a quando non viene impostato un nuovo gestore o la porta viene eliminata.
port.unref()
Aggiunto in: v10.5.0
Chiamare unref()
su una porta consente al thread di uscire se questa è l'unica handle attiva nel sistema di eventi. Se la porta è già unref()
, chiamare nuovamente unref()
non ha effetto.
Se gli ascoltatori vengono collegati o rimossi utilizzando .on('message')
, la porta viene ref()
e unref()
automaticamente a seconda che esistano ascoltatori per l'evento.
Classe: Worker
Aggiunto in: v10.5.0
- Estensioni: <EventEmitter>
La classe Worker
rappresenta un thread di esecuzione JavaScript indipendente. La maggior parte delle API di Node.js sono disponibili al suo interno.
Differenze notevoli all'interno di un ambiente Worker:
- I flussi
process.stdin
,process.stdout
eprocess.stderr
possono essere reindirizzati dal thread principale. - La proprietà
require('node:worker_threads').isMainThread
è impostata sufalse
. - La porta del messaggio
require('node:worker_threads').parentPort
è disponibile. process.exit()
non interrompe l'intero programma, solo il singolo thread, eprocess.abort()
non è disponibile.process.chdir()
e i metodiprocess
che impostano gli ID di gruppo o utente non sono disponibili.process.env
è una copia delle variabili di ambiente del thread principale, salvo diversa indicazione. Le modifiche a una copia non sono visibili negli altri thread e non sono visibili ai componenti aggiuntivi nativi (a meno che non venga passatoworker.SHARE_ENV
come opzioneenv
al costruttoreWorker
). Su Windows, a differenza del thread principale, una copia delle variabili di ambiente funziona in modo case-sensitive.process.title
non può essere modificato.- I segnali non vengono recapitati tramite
process.on('...')
. - L'esecuzione può interrompersi in qualsiasi momento a causa 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 di 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 integrata di MessagePort
che sono già associate tra loro quando viene creato il Worker
. Mentre l'oggetto MessagePort
sul lato principale non è direttamente esposto, le sue funzionalità sono esposte tramite worker.postMessage()
e l'evento worker.on('message')
sull'oggetto Worker
per il thread principale.
Per creare canali di messaggistica personalizzati (cosa che è consigliata rispetto all'utilizzo del canale globale predefinito perché facilita la separazione delle preoccupazioni), gli utenti possono creare un oggetto MessageChannel
su entrambi i thread e passare uno dei MessagePort
su tale MessageChannel
all'altro thread tramite un canale preesistente, come quello globale.
Vedere port.postMessage()
per ulteriori informazioni su come vengono passati i messaggi e su quale tipo di valori JavaScript possono essere trasportati correttamente 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 l'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 | Introdotta l'opzione trackUnmanagedFds . |
v13.13.0, v12.17.0 | 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 | Introdotta l'opzione argv . |
v13.2.0, v12.16.0 | Introdotta l'opzione resourceLimits . |
v10.5.0 | Aggiunto in: v10.5.0 |
filename
<string> | <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 unURL data:
, i dati vengono interpretati in base al tipo MIME utilizzando il caricatore di moduli ECMAScript. Seoptions.eval
ètrue
, questa è una stringa contenente codice JavaScript anziché un percorso.options
<Object>argv
<any[]> Elenco di argomenti che verrebbero convertiti in stringa e aggiunti aprocess.argv
nel worker. Questo è molto simile aworkerData
, ma i valori sono disponibili nelprocess.argv
globale come se fossero stati passati come opzioni CLI allo script.env
<Object> Se impostato, specifica il valore iniziale diprocess.env
all'interno del thread Worker. Come valore speciale,worker.SHARE_ENV
può essere utilizzato 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 influenzano anche l'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 di opzioni della riga di comando di Node passate al worker. Le opzioni V8 (come--max-old-space-size
) e le opzioni che influenzano il 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 passato aprocess.stdout
nel padre.stderr
<boolean> Se questo è impostato sutrue
, alloraworker.stderr
non viene automaticamente passato 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é contienefunzioni
).trackUnmanagedFds
<boolean> Se questo è impostato sutrue
, il Worker tiene traccia dei descrittori di file non gestiti tramitefs.open()
efs.close()
, e li chiude quando il Worker termina, in modo simile ad altre risorse come socket di rete o descrittori di file gestiti tramite l'APIFileHandle
. Questa opzione viene automaticamente ereditata da tutti iWorker
annidati. Predefinito:true
.transferList
<Object[]> Se uno o più oggetti simili aMessagePort
vengono passati inworkerData
, è richiesto untransferList
per tali elementi o viene generatoERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
[/api/errors#err_missing_message_port_in_transfer_list]. Vedereport.postMessage()
per maggiori informazioni.resourceLimits
<Object> Un set opzionale di limiti di risorse per la nuova istanza del motore JS. Raggiungere questi limiti porta alla terminazione dell'istanzaWorker
. Questi limiti influenzano solo il motore JS e nessun dato esterno, inclusi iArrayBuffer
. Anche se questi limiti sono impostati, il processo potrebbe comunque interrompersi se incontra una situazione di memoria insufficiente globale.maxOldGenerationSizeMb
<number> La dimensione massima dell'heap principale in MB. Se è impostato l'argomento della riga di comando--max-old-space-size
, questo sovrascrive questa impostazione.maxYoungGenerationSizeMb
<number> La dimensione massima di uno spazio heap per gli oggetti creati di recente. Se è impostato l'argomento della riga di comando--max-semi-space-size
, questo sovrascrive questa impostazione.codeRangeSizeMb
<number> La dimensione di un intervallo di memoria pre-allocato utilizzato per il codice generato.stackSizeMb
<number> La dimensione massima dello stack predefinita per il thread. Valori piccoli possono portare a istanze Worker inutilizzabili. Predefinito:4
.name
<string> Un nome 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 gestita. In tal caso, il worker viene terminato.
Evento: 'exit'
Aggiunto in: v10.5.0
exitCode
<intero>
L'evento 'exit'
viene emesso una volta che il worker si è arrestato. 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'ultimo evento emesso da qualsiasi istanza Worker
.
Evento: 'message'
Aggiunto in: v10.5.0
value
<qualsiasi> 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'evento 'exit'
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 ha avuto esito negativo.
Evento: 'online'
Aggiunto in: v10.5.0
L'evento 'online'
viene emesso quando il thread worker ha iniziato a eseguire il codice JavaScript.
worker.getHeapSnapshot([options])
[Cronologia]
Versione | Modifiche |
---|---|
v19.1.0 | Supporto opzioni per configurare lo snapshot dell'heap. |
v13.9.0, v12.17.0 | Aggiunto in: v13.9.0, v12.17.0 |
options
<Oggetto>exposeInternals
<booleano> Se vero, espone gli interni nello snapshot dell'heap. Default:false
.exposeNumericValues
<booleano> Se vero, espone i valori numerici in campi artificiali. Default:false
.
Restituisce: <Promise> Una promessa per un flusso leggibile contenente uno snapshot dell'heap V8
Restituisce un flusso leggibile per uno snapshot V8 dello stato corrente del Worker. Vedi v8.getHeapSnapshot()
per maggiori dettagli.
Se il thread Worker non è più in esecuzione, il che può verificarsi prima che venga emesso l'evento 'exit'
, 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 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 chiamata precedente aeventLoopUtilization()
.utilization2
<Object> Il risultato di una chiamata precedente aeventLoopUtilization()
prima diutilization1
.- Restituisce: <Object>
La stessa chiamata di perf_hooks
eventLoopUtilization()
, eccetto che vengono restituiti i valori dell'istanza worker.
Una differenza è che, a differenza del thread principale, l'avvio all'interno di un worker viene eseguito all'interno dell'event loop. Quindi l'utilizzo dell'event loop è immediatamente disponibile una volta che lo script del worker inizia l'esecuzione.
Un tempo idle
che non aumenta non indica che il worker è bloccato nell'avvio. Gli esempi seguenti mostrano come l'intera durata del worker non accumula mai alcun tempo idle
, ma è 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 dell'event loop di un worker è disponibile solo dopo che l'evento 'online'
è stato emesso, e se chiamato prima di questo, o dopo l'evento 'exit'
, allora 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
Opposto di unref()
, chiamare ref()
su un worker precedentemente unref()
ed non consente al programma di uscire se è l'unico handle attivo rimanente (il comportamento predefinito). Se il worker è ref()
ed, chiamare nuovamente ref()
non ha effetto.
worker.resourceLimits
Aggiunto in: v13.2.0, v12.16.0
Fornisce l'insieme dei vincoli di 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 è arrestato, il valore restituito è un oggetto vuoto.
worker.stderr
Aggiunto in: v10.5.0
Questo è un flusso leggibile che contiene i dati scritti su process.stderr
all'interno del thread worker. Se stderr: true
non è stato passato al costruttore Worker
, i dati vengono inviati al flusso process.stderr
del thread principale.
worker.stdin
Aggiunto in: v10.5.0
Se stdin: true
è stato passato 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
, i dati vengono inviati al flusso process.stdout
del thread principale.
worker.terminate()
[Cronologia]
Versione | Modifiche |
---|---|
v12.5.0 | Questa funzione ora restituisce una Promise. Passare una callback è deprecato ed era 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>
Arresta tutta l'esecuzione JavaScript nel thread worker il prima possibile. Restituisce una Promise per il codice di uscita che viene soddisfatta quando viene emesso l'evento 'exit'
.
worker.threadId
Aggiunto in: v10.5.0
Un identificatore intero per il thread di riferimento. All'interno del thread worker, è disponibile come require('node:worker_threads').threadId
. Questo valore è univoco per ogni istanza di 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 nuovamente unref()
non ha alcun 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 sul lato ricevente che sta bloccando il loop degli eventi 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++) {
// Loop 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++) {
// Loop 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 al crash dell'applicazione.