Threads de travail
[Stable : 2 - Stable]
Stable : 2 Stabilité : 2 - Stable
Code source : lib/worker_threads.js
Le module node:worker_threads
permet l'utilisation de threads qui exécutent JavaScript en parallèle. Pour y accéder :
const worker = require('node:worker_threads')
Les Workers (threads) sont utiles pour effectuer des opérations JavaScript gourmandes en CPU. Ils n'aident pas beaucoup avec les tâches gourmandes en E/S. Les opérations d'E/S asynchrones intégrées de Node.js sont plus efficaces que ce que les Workers peuvent être.
Contrairement à child_process
ou cluster
, worker_threads
peut partager la mémoire. Ils le font en transférant des instances ArrayBuffer
ou en partageant des instances 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 stopped with exit code ${code}`))
})
})
}
} else {
const { parse } = require('some-js-parsing-library')
const script = workerData
parentPort.postMessage(parse(script))
}
L'exemple ci-dessus génère un thread Worker pour chaque appel parseJSAsync()
. En pratique, utilisez un pool de Workers pour ce type de tâches. Sinon, les frais généraux de création de Workers dépasseraient probablement leurs avantages.
Lors de la mise en œuvre d'un pool de workers, utilisez l'API AsyncResource
pour informer les outils de diagnostic (par exemple, pour fournir des traces de pile asynchrones) de la corrélation entre les tâches et leurs résultats. Voir "Utilisation de AsyncResource
pour un pool de threads Worker
" dans la documentation async_hooks
pour un exemple d'implémentation.
Les threads Worker héritent par défaut des options non spécifiques au processus. Reportez-vous à Options du constructeur Worker
pour savoir comment personnaliser les options des threads Worker, en particulier les options argv
et execArgv
.
worker.getEnvironmentData(key)
[Historique]
Version | Modifications |
---|---|
v17.5.0, v16.15.0 | N'est plus expérimental. |
v15.12.0, v14.18.0 | Ajouté dans : v15.12.0, v14.18.0 |
key
<any> Toute valeur JavaScript arbitraire et clonable pouvant servir de clé <Map>.- Retourne : <any>
Dans un thread worker, worker.getEnvironmentData()
retourne un clone des données passées au thread de génération worker.setEnvironmentData()
. Chaque nouveau Worker
reçoit automatiquement sa propre copie des données d'environnement.
const { Worker, isMainThread, setEnvironmentData, getEnvironmentData } = require('node:worker_threads')
if (isMainThread) {
setEnvironmentData('Hello', 'World!')
const worker = new Worker(__filename)
} else {
console.log(getEnvironmentData('Hello')) // Affiche 'World!'.
}
worker.isMainThread
Ajouté dans : v10.5.0
Est égal à true
si ce code ne s'exécute pas à l'intérieur d'un thread Worker
.
const { Worker, isMainThread } = require('node:worker_threads')
if (isMainThread) {
// Ceci recharge le fichier actuel à l'intérieur d'une instance Worker.
new Worker(__filename)
} else {
console.log('Inside Worker!')
console.log(isMainThread) // Affiche 'false'.
}
worker.markAsUntransferable(object)
Ajouté dans : v14.5.0, v12.19.0
object
<any> Toute valeur JavaScript arbitraire.
Marquer un objet comme non transférable. Si object
apparaît dans la liste de transfert d'un appel port.postMessage()
, une erreur est levée. Il s'agit d'une opération sans effet si object
est une valeur primitive.
En particulier, cela a du sens pour les objets qui peuvent être clonés, plutôt que transférés, et qui sont utilisés par d'autres objets du côté envoyeur. Par exemple, Node.js marque les ArrayBuffer
qu'il utilise pour son pool Buffer
avec ceci.
Cette opération est irréversible.
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 {
// Ceci lèvera une erreur, car pooledBuffer n'est pas transférable.
port1.postMessage(typedArray1, [typedArray1.buffer])
} catch (error) {
// error.name === 'DataCloneError'
}
// La ligne suivante affiche le contenu de typedArray1 -- il possède toujours
// sa mémoire et n'a pas été transféré. Sans
// `markAsUntransferable()`, ceci afficherait un Uint8Array vide et l'appel
// postMessage aurait réussi.
// typedArray2 est également intact.
console.log(typedArray1)
console.log(typedArray2)
Il n'existe pas d'équivalent à cette API dans les navigateurs.
worker.isMarkedAsUntransferable(object)
Ajouté dans : v21.0.0
Vérifie si un objet est marqué comme non transférable avec markAsUntransferable()
.
const { markAsUntransferable, isMarkedAsUntransferable } = require('node:worker_threads')
const pooledBuffer = new ArrayBuffer(8)
markAsUntransferable(pooledBuffer)
isMarkedAsUntransferable(pooledBuffer) // Retourne true.
Il n’existe pas d’équivalent à cette API dans les navigateurs.
worker.markAsUncloneable(object)
Ajouté dans : v23.0.0
object
<any> Toute valeur JavaScript arbitraire.
Marque un objet comme non clonable. Si object
est utilisé comme message
dans un appel port.postMessage()
, une erreur est levée. Ceci est une opération sans effet si object
est une valeur primitive.
Ceci n’a aucun effet sur ArrayBuffer
, ou tout objet de type Buffer
.
Cette opération est irréversible.
const { markAsUncloneable } = require('node:worker_threads')
const anyObject = { foo: 'bar' }
markAsUncloneable(anyObject)
const { port1 } = new MessageChannel()
try {
// Ceci lèvera une erreur, car anyObject n’est pas clonable.
port1.postMessage(anyObject)
} catch (error) {
// error.name === 'DataCloneError'
}
Il n’existe pas d’équivalent à cette API dans les navigateurs.
worker.moveMessagePortToContext(port, contextifiedSandbox)
Ajouté dans : v11.13.0
port
<MessagePort> Le port de message à transférer.contextifiedSandbox
<Object> Un objet contextified comme retourné par la méthodevm.createContext()
.- Retourne : <MessagePort>
Transfère un MessagePort
vers un contexte vm
différent. L’objet port
original devient inutilisable, et l’instance MessagePort
retournée prend sa place.
Le MessagePort
retourné est un objet dans le contexte cible et hérite de sa classe globale Object
. Les objets passés à l’écouteur port.onmessage()
sont également créés dans le contexte cible et héritent de sa classe globale Object
.
Cependant, le MessagePort
créé n’hérite plus de EventTarget
, et seul port.onmessage()
peut être utilisé pour recevoir des événements en l’utilisant.
worker.parentPort
Ajouté dans : v10.5.0
Si ce thread est un Worker
, il s'agit d'un MessagePort
permettant la communication avec le thread parent. Les messages envoyés à l'aide de parentPort.postMessage()
sont disponibles dans le thread parent à l'aide de worker.on('message')
, et les messages envoyés depuis le thread parent à l'aide de worker.postMessage()
sont disponibles dans ce thread à l'aide de 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) // Affiche 'Hello, world!'.
})
worker.postMessage('Hello, world!')
} else {
// Lorsqu'un message du thread parent est reçu, renvoyez-le :
parentPort.once('message', message => {
parentPort.postMessage(message)
})
}
worker.postMessageToThread(threadId, value[, transferList][, timeout])
Ajouté dans : v22.5.0
[Stable : 1 - Expérimental]
Stable : 1 Stabilité : 1.1 - Développement actif
threadId
<number> L'ID du thread cible. Si l'ID du thread est invalide, une erreurERR_WORKER_MESSAGING_FAILED
sera levée. Si l'ID du thread cible est l'ID du thread actuel, une erreurERR_WORKER_MESSAGING_SAME_THREAD
sera levée.value
<any> La valeur à envoyer.transferList
<Object[]> Si un ou plusieurs objets de typeMessagePort
sont passés dansvalue
, unetransferList
est requise pour ces éléments ouERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
est levée. Voirport.postMessage()
pour plus d'informations.timeout
<number> Temps d'attente pour la livraison du message en millisecondes. Par défaut, c'estundefined
, ce qui signifie attendre indéfiniment. Si l'opération dépasse le délai imparti, une erreurERR_WORKER_MESSAGING_TIMEOUT
est levée.- Retourne : <Promise> Une promesse qui est remplie si le message a été traité avec succès par le thread de destination.
Envoie une valeur à un autre travailleur, identifié par son ID de thread.
Si le thread cible n'a aucun écouteur pour l'événement workerMessage
, l'opération lèvera une erreur ERR_WORKER_MESSAGING_FAILED
.
Si le thread cible a levé une erreur lors du traitement de l'événement workerMessage
, l'opération lèvera une erreur ERR_WORKER_MESSAGING_ERRORED
.
Cette méthode doit être utilisée lorsque le thread cible n'est pas le parent ou l'enfant direct du thread actuel. Si les deux threads sont parents-enfants, utilisez require('node:worker_threads').parentPort.postMessage()
et worker.postMessage()
pour permettre aux threads de communiquer.
L'exemple ci-dessous montre l'utilisation de postMessageToThread
: il crée 10 threads imbriqués, le dernier essaiera de communiquer avec le thread principal.
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)
[Historique]
Version | Modifications |
---|---|
v15.12.0 | L'argument port peut désormais également faire référence à un BroadcastChannel . |
v12.3.0 | Ajouté dans : v12.3.0 |
port
<MessagePort> | <BroadcastChannel>- Retourne : <Object> | <undefined>
Reçoit un seul message d'un MessagePort
donné. Si aucun message n'est disponible, undefined
est retourné, sinon un objet avec une seule propriété message
contenant la charge utile du message, correspondant au message le plus ancien de la file d'attente du MessagePort
.
const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
port1.postMessage({ hello: 'world' })
console.log(receiveMessageOnPort(port2))
// Affiche : { message: { hello: 'world' } }
console.log(receiveMessageOnPort(port2))
// Affiche : undefined
Lorsque cette fonction est utilisée, aucun événement 'message'
n'est émis et le gestionnaire onmessage
n'est pas appelé.
worker.resourceLimits
Ajouté dans : v13.2.0, v12.16.0
Fournit l'ensemble des contraintes de ressources du moteur JS dans ce thread Worker. Si l'option resourceLimits
a été passée au constructeur Worker
, cela correspond à ses valeurs.
Si cela est utilisé dans le thread principal, sa valeur est un objet vide.
worker.SHARE_ENV
Ajouté dans : v11.14.0
Une valeur spéciale pouvant être passée en tant qu’option env
du constructeur Worker
, pour indiquer que le thread actuel et le thread Worker doivent partager un accès en lecture et en écriture au même ensemble de variables d’environnement.
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) // Affiche 'foo'.
})
worker.setEnvironmentData(key[, value])
[Historique]
Version | Modifications |
---|---|
v17.5.0, v16.15.0 | N’est plus expérimental. |
v15.12.0, v14.18.0 | Ajouté dans : v15.12.0, v14.18.0 |
key
<any> Toute valeur JavaScript arbitraire et clonable pouvant être utilisée comme clé <Map>.value
<any> Toute valeur JavaScript arbitraire et clonable qui sera clonée et passée automatiquement à toutes les nouvelles instancesWorker
. Sivalue
est passé commeundefined
, toute valeur précédemment définie pour la clékey
sera supprimée.
L’API worker.setEnvironmentData()
définit le contenu de worker.getEnvironmentData()
dans le thread actuel et toutes les nouvelles instances Worker
générées à partir du contexte actuel.
worker.threadId
Ajouté dans : v10.5.0
Un identifiant entier pour le thread actuel. Sur l’objet worker correspondant (le cas échéant), il est disponible sous la forme worker.threadId
. Cette valeur est unique pour chaque instance Worker
au sein d’un seul processus.
worker.workerData
Ajouté dans : v10.5.0
Une valeur JavaScript arbitraire qui contient un clone des données passées au constructeur Worker
de ce thread.
Les données sont clonées comme si on utilisait postMessage()
, conformément à l'algorithme de clonage structuré HTML.
const { Worker, isMainThread, workerData } = require('node:worker_threads')
if (isMainThread) {
const worker = new Worker(__filename, { workerData: 'Hello, world!' })
} else {
console.log(workerData) // Affiche 'Hello, world!'.
}
Classe : BroadcastChannel extends EventTarget
[Historique]
Version | Modifications |
---|---|
v18.0.0 | N'est plus expérimental. |
v15.4.0 | Ajouté dans : v15.4.0 |
Les instances de BroadcastChannel
permettent une communication asynchrone un-à-plusieurs avec toutes les autres instances de BroadcastChannel
liées au même nom de canal.
'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)
Ajouté dans : v15.4.0
name
<any> Le nom du canal auquel se connecter. Toute valeur JavaScript pouvant être convertie en chaîne de caractères à l'aide de${name}
est autorisée.
broadcastChannel.close()
Ajouté dans : v15.4.0
Ferme la connexion BroadcastChannel
.
broadcastChannel.onmessage
Ajouté dans : v15.4.0
- Type : <Function> Appelée avec un seul argument
MessageEvent
lorsqu'un message est reçu.
broadcastChannel.onmessageerror
Ajouté dans : v15.4.0
- Type : <Function> Invoqué lorsqu'un message reçu ne peut pas être désérialisé.
broadcastChannel.postMessage(message)
Ajouté dans : v15.4.0
message
<any> Toute valeur JavaScript clonable.
broadcastChannel.ref()
Ajouté dans : v15.4.0
Opposé de unref()
. Appeler ref()
sur un BroadcastChannel précédemment unref()
ne permet pas au programme de se terminer s'il s'agit de la seule poignée active restante (comportement par défaut). Si le port est ref()
é, appeler ref()
à nouveau n'a aucun effet.
broadcastChannel.unref()
Ajouté dans : v15.4.0
Appeler unref()
sur un BroadcastChannel permet au thread de se terminer s'il s'agit de la seule poignée active dans le système d'événements. Si le BroadcastChannel est déjà unref()
é, appeler unref()
à nouveau n'a aucun effet.
Classe : MessageChannel
Ajouté dans : v10.5.0
Les instances de la classe worker.MessageChannel
représentent un canal de communication bidirectionnel asynchrone. MessageChannel
n'a pas de méthodes propres. new MessageChannel()
renvoie un objet avec les propriétés port1
et port2
, qui font référence à des instances liées de MessagePort
.
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
port1.on('message', message => console.log('received', message))
port2.postMessage({ foo: 'bar' })
// Affiche : received { foo: 'bar' } depuis le listener `port1.on('message')`
Classe : MessagePort
[Historique]
Version | Modifications |
---|---|
v14.7.0 | Cette classe hérite maintenant de EventTarget plutôt que de EventEmitter . |
v10.5.0 | Ajouté dans : v10.5.0 |
- Étend : <EventTarget>
Les instances de la classe worker.MessagePort
représentent une extrémité d'un canal de communication bidirectionnel asynchrone. Il peut être utilisé pour transférer des données structurées, des régions de mémoire et d'autres MessagePort
entre différents Worker
.
Cette implémentation correspond aux MessagePort
du navigateur.
Événement : 'close'
Ajouté dans : v10.5.0
L'événement 'close'
est émis une fois que l'un ou l'autre côté du canal a été déconnecté.
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
// Affiche :
// foobar
// fermé !
port2.on('message', message => console.log(message))
port2.on('close', () => console.log('fermé !'))
port1.postMessage('foobar')
port1.close()
Événement : 'message'
Ajouté dans : v10.5.0
value
<any> La valeur transmise
L'événement 'message'
est émis pour tout message entrant, contenant l'entrée clonée de port.postMessage()
.
Les écouteurs de cet événement reçoivent un clone du paramètre value
tel que passé à postMessage()
et aucun autre argument.
Événement : 'messageerror'
Ajouté dans : v14.5.0, v12.19.0
error
<Error> Un objet Error
L'événement 'messageerror'
est émis lorsque la désérialisation d'un message a échoué.
Actuellement, cet événement est émis lorsqu'une erreur se produit lors de l'instanciation de l'objet JS publié sur le côté récepteur. De telles situations sont rares, mais peuvent se produire, par exemple, lorsque certains objets de l'API Node.js sont reçus dans un vm.Context
(où les API Node.js ne sont actuellement pas disponibles).
port.close()
Ajouté dans : v10.5.0
Désactive l'envoi ultérieur de messages de l'un ou l'autre côté de la connexion. Cette méthode peut être appelée lorsqu'aucune autre communication n'aura lieu via ce MessagePort
.
L'événement 'close'
est émis sur les deux instances MessagePort
qui font partie du canal.
port.postMessage(value[, transferList])
[Historique]
Version | Modifications |
---|---|
v21.0.0 | Une erreur est levée lorsqu'un objet non transférable est dans la liste de transfert. |
v15.6.0 | Ajout de X509Certificate à la liste des types clonables. |
v15.0.0 | Ajout de CryptoKey à la liste des types clonables. |
v15.14.0, v14.18.0 | Ajout de 'BlockList' à la liste des types clonables. |
v15.9.0, v14.18.0 | Ajout des types 'Histogram' à la liste des types clonables. |
v14.5.0, v12.19.0 | Ajout de KeyObject à la liste des types clonables. |
v14.5.0, v12.19.0 | Ajout de FileHandle à la liste des types transférables. |
v10.5.0 | Ajouté dans : v10.5.0 |
value
<any>transferList
<Object[]>
Envoie une valeur JavaScript au côté récepteur de ce canal. value
est transféré d'une manière compatible avec l'algorithme de clonage structuré HTML.
En particulier, les différences significatives par rapport à JSON
sont :
value
peut contenir des références circulaires.value
peut contenir des instances de types JS intégrés tels que desRegExp
, desBigInt
, desMap
, desSet
, etc.value
peut contenir des tableaux typés, à la fois en utilisant desArrayBuffer
et desSharedArrayBuffer
.value
peut contenir des instances deWebAssembly.Module
.value
ne peut pas contenir d'objets natifs (supportés par C++) autres que :
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
port1.on('message', message => console.log(message))
const circularData = {}
circularData.foo = circularData
// Affiche : { foo: [Circular] }
port2.postMessage(circularData)
transferList
peut être une liste d'objets ArrayBuffer
, MessagePort
et FileHandle
. Après le transfert, ils ne sont plus utilisables du côté envoi du canal (même s'ils ne sont pas contenus dans value
). Contrairement aux processus enfants, le transfert de handles tels que les sockets réseau n'est actuellement pas pris en charge.
Si value
contient des instances de SharedArrayBuffer
, celles-ci sont accessibles depuis l'un ou l'autre thread. Elles ne peuvent pas être listées dans transferList
.
value
peut toujours contenir des instances ArrayBuffer
qui ne sont pas dans transferList
; dans ce cas, la mémoire sous-jacente est copiée plutôt que déplacée.
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])
// Ceci publie une copie de `uint8Array` :
port2.postMessage(uint8Array)
// Ceci ne copie pas les données, mais rend `uint8Array` inutilisable :
port2.postMessage(uint8Array, [uint8Array.buffer])
// La mémoire pour `sharedUint8Array` est accessible à la fois depuis l'original
// et la copie reçue par `.on('message')` :
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4))
port2.postMessage(sharedUint8Array)
// Ceci transfère un port de message nouvellement créé au récepteur.
// Ceci peut être utilisé, par exemple, pour créer des canaux de communication entre
// plusieurs threads `Worker` qui sont enfants du même thread parent.
const otherChannel = new MessageChannel()
port2.postMessage({ port: otherChannel.port1 }, [otherChannel.port1])
L'objet message est cloné immédiatement et peut être modifié après la publication sans avoir d'effets secondaires.
Pour plus d'informations sur les mécanismes de sérialisation et de désérialisation derrière cette API, consultez l'API de sérialisation du module node:v8
.
Considérations lors du transfert de TypedArrays et de Buffers
Toutes les instances de TypedArray
et de Buffer
sont des vues sur un ArrayBuffer
sous-jacent. C'est-à-dire que c'est l'ArrayBuffer
qui stocke réellement les données brutes, tandis que les objets TypedArray
et Buffer
fournissent un moyen de visualiser et de manipuler les données. Il est possible et courant de créer plusieurs vues sur la même instance d'ArrayBuffer
. Il faut faire très attention lors de l'utilisation d'une liste de transfert pour transférer un ArrayBuffer
, car cela rend inutilisables toutes les instances de TypedArray
et de Buffer
qui partagent ce même ArrayBuffer
.
const ab = new ArrayBuffer(10)
const u1 = new Uint8Array(ab)
const u2 = new Uint16Array(ab)
console.log(u2.length) // affiche 5
port.postMessage(u1, [u1.buffer])
console.log(u2.length) // affiche 0
Pour les instances de Buffer
, plus précisément, la possibilité de transférer ou de cloner l'ArrayBuffer
sous-jacent dépend entièrement de la façon dont les instances ont été créées, ce qui ne peut souvent pas être déterminé de manière fiable.
Un ArrayBuffer
peut être marqué avec markAsUntransferable()
pour indiquer qu'il doit toujours être cloné et jamais transféré.
Selon la façon dont une instance de Buffer
a été créée, il se peut qu'elle possède ou non son ArrayBuffer
sous-jacent. Un ArrayBuffer
ne doit pas être transféré à moins qu'il ne soit connu que l'instance Buffer
le possède. En particulier, pour les Buffer
créés à partir du pool interne Buffer
(en utilisant, par exemple, Buffer.from()
ou Buffer.allocUnsafe()
), leur transfert n'est pas possible et ils sont toujours clonés, ce qui envoie une copie de l'ensemble du pool Buffer
. Ce comportement peut entraîner une utilisation de la mémoire plus élevée et des problèmes de sécurité imprévus.
Voir Buffer.allocUnsafe()
pour plus de détails sur la mise en commun des Buffer
.
Les ArrayBuffer
pour les instances de Buffer
créées à l'aide de Buffer.alloc()
ou Buffer.allocUnsafeSlow()
peuvent toujours être transférées, mais cela rend inutilisables toutes les autres vues existantes de ces ArrayBuffer
.
Considérations lors du clonage d'objets avec prototypes, classes et accesseurs
Le clonage d'objets utilise l'algorithme de clonage structuré HTML Structured clone algorithm, les propriétés non énumérables, les accesseurs de propriétés et les prototypes d'objets ne sont pas préservés. En particulier, les objets Buffer
seront lus comme des Uint8Array
simples du côté récepteur, et les instances de classes JavaScript seront clonées comme des objets JavaScript simples.
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())
// Affiche : { c: 3 }
Cette limitation s'étend à de nombreux objets intégrés, tels que l'objet global URL
:
const { port1, port2 } = new MessageChannel()
port1.onmessage = ({ data }) => console.log(data)
port2.postMessage(new URL('https://example.org'))
// Affiche : { }
port.hasRef()
Ajouté dans : v18.1.0, v16.17.0
[Stable : 1 - Expérimental]
Stable : 1 Stabilité : 1 - Expérimental
- Retourne : <boolean>
Si vrai, l'objet MessagePort
maintiendra la boucle d'événements Node.js active.
port.ref()
Ajouté dans : v10.5.0
Opposé de unref()
. Appeler ref()
sur un port précédemment unref()
ne permet pas au programme de se terminer s'il s'agit de la seule poignée active restante (comportement par défaut). Si le port est ref()
, appeler ref()
à nouveau n'a aucun effet.
Si des écouteurs sont attachés ou supprimés à l'aide de .on('message')
, le port est ref()
et unref()
automatiquement selon que des écouteurs pour l'événement existent.
port.start()
Ajouté dans : v10.5.0
Commence à recevoir des messages sur ce MessagePort
. Lorsque ce port est utilisé comme émetteur d'événements, cela est appelé automatiquement une fois que les écouteurs 'message'
sont attachés.
Cette méthode existe pour la parité avec l'API Web MessagePort
. Dans Node.js, elle n'est utile que pour ignorer les messages lorsqu'aucun écouteur d'événements n'est présent. Node.js diverge également dans sa gestion de .onmessage
. Sa définition appelle automatiquement .start()
, mais sa suppression permet aux messages de s'accumuler jusqu'à ce qu'un nouveau gestionnaire soit défini ou que le port soit supprimé.
port.unref()
Ajouté dans : v10.5.0
Appeler unref()
sur un port permet au thread de sortir si c'est la seule poignée active dans le système d'événements. Si le port est déjà unref()
, appeler unref()
à nouveau n'a aucun effet.
Si des écouteurs sont attachés ou supprimés à l'aide de .on('message')
, le port est ref()
et unref()
automatiquement en fonction de l'existence d'écouteurs pour l'événement.
Classe : Worker
Ajouté dans : v10.5.0
- Étend : <EventEmitter>
La classe Worker
représente un thread d'exécution JavaScript indépendant. La plupart des API Node.js sont disponibles à l'intérieur.
Différences notables dans un environnement Worker :
- Les flux
process.stdin
,process.stdout
etprocess.stderr
peuvent être redirigés par le thread parent. - La propriété
require('node:worker_threads').isMainThread
est définie surfalse
. - Le port de message
require('node:worker_threads').parentPort
est disponible. process.exit()
n'arrête pas l'ensemble du programme, seulement le thread unique, etprocess.abort()
n'est pas disponible.process.chdir()
et les méthodesprocess
qui définissent les identifiants de groupe ou d'utilisateur ne sont pas disponibles.process.env
est une copie des variables d'environnement du thread parent, sauf indication contraire. Les modifications apportées à une copie ne sont pas visibles dans les autres threads et ne sont pas visibles pour les modules complémentaires natifs (sauf siworker.SHARE_ENV
est passé comme optionenv
au constructeurWorker
). Sous Windows, contrairement au thread principal, une copie des variables d'environnement fonctionne de manière sensible à la casse.process.title
ne peut pas être modifié.- Les signaux ne sont pas transmis via
process.on('...')
. - L'exécution peut s'arrêter à tout moment suite à l'appel de
worker.terminate()
. - Les canaux IPC des processus parents ne sont pas accessibles.
- Le module
trace_events
n'est pas pris en charge. - Les modules complémentaires natifs ne peuvent être chargés à partir de plusieurs threads que s'ils remplissent certaines conditions.
La création d'instances Worker
à l'intérieur d'autres Worker
est possible.
Comme les Web Workers et le module node:cluster
, une communication bidirectionnelle peut être réalisée grâce à l'échange de messages inter-threads. En interne, un Worker
possède une paire intégrée de MessagePort
qui sont déjà associés l'un à l'autre lors de la création du Worker
. Bien que l'objet MessagePort
du côté parent ne soit pas directement exposé, ses fonctionnalités sont exposées via worker.postMessage()
et l'événement worker.on('message')
sur l'objet Worker
pour le thread parent.
Pour créer des canaux de messagerie personnalisés (ce qui est encouragé par rapport à l'utilisation du canal global par défaut car cela facilite la séparation des préoccupations), les utilisateurs peuvent créer un objet MessageChannel
sur l'un ou l'autre thread et transmettre l'un des MessagePort
de ce MessageChannel
à l'autre thread via un canal préexistant, tel que le canal global.
Voir port.postMessage()
pour plus d'informations sur la manière dont les messages sont transmis et sur le type de valeurs JavaScript qui peuvent être transportées avec succès à travers la barrière des threads.
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])
[Historique]
Version | Modifications |
---|---|
v19.8.0, v18.16.0 | Ajout de la prise en charge d'une option name , permettant d'ajouter un nom au titre du worker pour le débogage. |
v14.9.0 | Le paramètre filename peut être un objet URL WHATWG utilisant le protocole data: . |
v14.9.0 | L'option trackUnmanagedFds était définie sur true par défaut. |
v14.6.0, v12.19.0 | L'option trackUnmanagedFds a été introduite. |
v13.13.0, v12.17.0 | L'option transferList a été introduite. |
v13.12.0, v12.17.0 | Le paramètre filename peut être un objet URL WHATWG utilisant le protocole file: . |
v13.4.0, v12.16.0 | L'option argv a été introduite. |
v13.2.0, v12.16.0 | L'option resourceLimits a été introduite. |
v10.5.0 | Ajouté dans : v10.5.0 |
filename
<string> | <URL> Le chemin vers le script principal ou le module du Worker. Doit être soit un chemin absolu, soit un chemin relatif (c'est-à-dire relatif au répertoire de travail actuel) commençant par./
ou../
, soit un objetURL
WHATWG utilisant le protocolefile:
oudata:
. Lors de l'utilisation d'une URLdata:
, les données sont interprétées en fonction du type MIME à l'aide du chargeur de modules ECMAScript. Sioptions.eval
esttrue
, il s'agit d'une chaîne contenant du code JavaScript plutôt qu'un chemin.options
<Object>argv
<any[]> Liste d'arguments qui seraient mis en chaîne et ajoutés àprocess.argv
dans le worker. Ceci est assez similaire àworkerData
, mais les valeurs sont disponibles sur leprocess.argv
global comme si elles étaient passées comme options CLI au script.env
<Object> Si défini, spécifie la valeur initiale deprocess.env
dans le thread Worker. En tant que valeur spéciale,worker.SHARE_ENV
peut être utilisé pour spécifier que le thread parent et le thread enfant doivent partager leurs variables d'environnement ; dans ce cas, les modifications apportées à l'objetprocess.env
d'un thread affectent également l'autre thread. Par défaut :process.env
.eval
<boolean> Sitrue
et que le premier argument est unestring
, interpréter le premier argument du constructeur comme un script qui est exécuté une fois le worker en ligne.execArgv
<string[]> Liste des options CLI de nœud passées au worker. Les options V8 (telles que--max-old-space-size
) et les options qui affectent le processus (telles que--title
) ne sont pas prises en charge. Si défini, ceci est fourni commeprocess.execArgv
dans le worker. Par défaut, les options sont héritées du thread parent.stdin
<boolean> Si ceci est défini surtrue
, alorsworker.stdin
fournit un flux inscriptible dont le contenu apparaît commeprocess.stdin
dans le Worker. Par défaut, aucune donnée n'est fournie.stdout
<boolean> Si ceci est défini surtrue
, alorsworker.stdout
n'est pas automatiquement acheminé versprocess.stdout
dans le parent.stderr
<boolean> Si ceci est défini surtrue
, alorsworker.stderr
n'est pas automatiquement acheminé versprocess.stderr
dans le parent.workerData
<any> Toute valeur JavaScript qui est clonée et mise à disposition en tant querequire('node:worker_threads').workerData
. Le clonage se produit comme décrit dans l'algorithme de clonage structuré HTML , et une erreur est levée si l'objet ne peut pas être cloné (par exemple, parce qu'il contient desfunction
).trackUnmanagedFds
<boolean> Si ceci est défini surtrue
, alors le Worker suit les descripteurs de fichiers bruts gérés viafs.open()
etfs.close()
, et les ferme lorsque le Worker se termine, de manière similaire aux autres ressources comme les sockets réseau ou les descripteurs de fichiers gérés via l'APIFileHandle
. Cette option est automatiquement héritée par tous lesWorker
imbriqués. Par défaut :true
.transferList
<Object[]> Si un ou plusieurs objets de typeMessagePort
sont passés dansworkerData
, unetransferList
est requise pour ces éléments, sinonERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
est levée. Voirport.postMessage()
pour plus d'informations.resourceLimits
<Object> Un ensemble facultatif de limites de ressources pour la nouvelle instance de moteur JS. Atteindre ces limites entraîne la terminaison de l'instanceWorker
. Ces limites n'affectent que le moteur JS, et aucune donnée externe, y compris aucunArrayBuffer
. Même si ces limites sont définies, le processus peut toujours s'arrêter s'il rencontre une situation globale de manque de mémoire.maxOldGenerationSizeMb
<number> La taille maximale du tas principal en Mo. Si l'argument de ligne de commande--max-old-space-size
est défini, il remplace ce paramètre.maxYoungGenerationSizeMb
<number> La taille maximale d'un espace de tas pour les objets récemment créés. Si l'argument de ligne de commande--max-semi-space-size
est défini, il remplace ce paramètre.codeRangeSizeMb
<number> La taille d'une plage de mémoire pré-allouée utilisée pour le code généré.stackSizeMb
<number> La taille de pile maximale par défaut pour le thread. De petites valeurs peuvent conduire à des instances Worker inutilisables. Par défaut :4
.name
<string> Unname
facultatif à ajouter au titre du worker à des fins de débogage/d'identification, rendant le titre final comme[worker ${id}] ${name}
. Par défaut :''
.
Événement : 'error'
Ajouté dans : v10.5.0
err
<Error>
L'événement 'error'
est émis si le thread worker lève une exception non interceptée. Dans ce cas, le worker est terminé.
Événement : 'exit'
Ajouté dans : v10.5.0
exitCode
<integer>
L'événement 'exit'
est émis une fois que le worker s'est arrêté. Si le worker a quitté en appelant process.exit()
, le paramètre exitCode
est le code de sortie passé. Si le worker a été terminé, le paramètre exitCode
est 1
.
C'est le dernier événement émis par toute instance Worker
.
Événement : 'message'
Ajouté dans : v10.5.0
value
<any> La valeur transmise
L'événement 'message'
est émis lorsque le thread worker a invoqué require('node:worker_threads').parentPort.postMessage()
. Voir l'événement port.on('message')
pour plus de détails.
Tous les messages envoyés depuis le thread worker sont émis avant que l'événement 'exit'
ne soit émis sur l'objet Worker
.
Événement : 'messageerror'
Ajouté dans : v14.5.0, v12.19.0
error
<Error> Un objet Error
L'événement 'messageerror'
est émis lorsque la désérialisation d'un message a échoué.
Événement : 'online'
Ajouté dans : v10.5.0
L'événement 'online'
est émis lorsque le thread worker a commencé à exécuter du code JavaScript.
worker.getHeapSnapshot([options])
[Historique]
Version | Modifications |
---|---|
v19.1.0 | Prise en charge des options pour configurer l'instantané du tas. |
v13.9.0, v12.17.0 | Ajouté dans : v13.9.0, v12.17.0 |
options
<Object>Retourne : <Promise> Une promesse pour un flux lisible contenant un instantané du tas V8
Retourne un flux lisible pour un instantané V8 de l'état actuel du Worker. Voir v8.getHeapSnapshot()
pour plus de détails.
Si le thread Worker n'est plus en cours d'exécution, ce qui peut se produire avant que l'événement 'exit'
ne soit émis, la Promise
retournée est rejetée immédiatement avec une erreur ERR_WORKER_NOT_RUNNING
.
worker.performance
Ajouté dans : v15.1.0, v14.17.0, v12.22.0
Un objet qui peut être utilisé pour interroger les informations de performance d'une instance de worker. Similaire à perf_hooks.performance
.
performance.eventLoopUtilization([utilization1[, utilization2]])
Ajouté dans : v15.1.0, v14.17.0, v12.22.0
utilization1
<Objet> Le résultat d'un appel précédent àeventLoopUtilization()
.utilization2
<Objet> Le résultat d'un appel précédent àeventLoopUtilization()
avantutilization1
.- Retourne : <Objet>
Le même appel que perf_hooks
eventLoopUtilization()
, sauf que les valeurs de l'instance worker sont retournées.
Une différence est que, contrairement au thread principal, l'amorçage au sein d'un worker est effectué au sein de la boucle d'événements. Ainsi, l'utilisation de la boucle d'événements est immédiatement disponible une fois que le script du worker commence son exécution.
Un temps idle
qui n'augmente pas n'indique pas que le worker est bloqué dans l'amorçage. Les exemples suivants montrent comment toute la durée de vie du worker n'accumule jamais de temps idle
, mais est toujours capable de traiter des messages.
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'utilisation de la boucle d'événements d'un worker n'est disponible qu'après l'émission de l'événement 'online'
, et si elle est appelée avant cela, ou après l'événement 'exit'
, alors toutes les propriétés ont la valeur 0
.
worker.postMessage(value[, transferList])
Ajouté dans : v10.5.0
value
<any>transferList
<Object[]>
Envoie un message au worker qui est reçu via require('node:worker_threads').parentPort.on('message')
. Voir port.postMessage()
pour plus de détails.
worker.ref()
Ajouté dans : v10.5.0
Opposé de unref()
, appeler ref()
sur un worker précédemment unref()
ed n'empêche pas le programme de se terminer s'il s'agit de la seule poignée active restante (comportement par défaut). Si le worker est ref()
é, appeler ref()
à nouveau n'a aucun effet.
worker.resourceLimits
Ajouté dans : v13.2.0, v12.16.0
Fournit l'ensemble des contraintes de ressources du moteur JS pour ce thread Worker. Si l'option resourceLimits
a été passée au constructeur Worker
, cela correspond à ses valeurs.
Si le worker s'est arrêté, la valeur de retour est un objet vide.
worker.stderr
Ajouté dans : v10.5.0
Il s'agit d'un flux lisible qui contient les données écrites dans process.stderr
à l'intérieur du thread worker. Si stderr: true
n'a pas été passé au constructeur Worker
, les données sont alors envoyées vers le flux process.stderr
du thread parent.
worker.stdin
Ajouté dans : v10.5.0
Si stdin: true
a été passé au constructeur Worker
, il s'agit d'un flux inscriptible. Les données écrites dans ce flux seront disponibles dans le thread worker en tant que process.stdin
.
worker.stdout
Ajouté dans : v10.5.0
Il s'agit d'un flux lisible qui contient les données écrites dans process.stdout
à l'intérieur du thread worker. Si stdout: true
n'a pas été passé au constructeur Worker
, les données sont alors envoyées par pipe vers le flux process.stdout
du thread parent.
worker.terminate()
[Historique]
Version | Modifications |
---|---|
v12.5.0 | Cette fonction retourne désormais une Promise. Le passage d'une fonction de rappel est obsolète et était inutile jusqu'à cette version, car le Worker était en réalité terminé de manière synchrone. La terminaison est maintenant une opération entièrement asynchrone. |
v10.5.0 | Ajouté dans : v10.5.0 |
- Retourne : <Promise>
Arrête toute exécution JavaScript dans le thread worker dès que possible. Retourne une Promise pour le code de sortie qui est rempli lorsque l'événement 'exit'
est émis.
worker.threadId
Ajouté dans : v10.5.0
Un identifiant entier pour le thread référencé. À l'intérieur du thread worker, il est disponible en tant que require('node:worker_threads').threadId
. Cette valeur est unique pour chaque instance Worker
au sein d'un seul processus.
worker.unref()
Ajouté dans : v10.5.0
Appeler unref()
sur un worker permet au thread de se terminer si c'est le seul handle actif dans le système d'événements. Si le worker est déjà unref()
, appeler unref()
à nouveau n'a aucun effet.
Remarques
Blocage synchrone de stdio
Les Worker
utilisent l'échange de messages via <MessagePort> pour implémenter les interactions avec stdio
. Cela signifie que la sortie stdio
provenant d'un Worker
peut être bloquée par du code synchrone du côté récepteur qui bloque la boucle d'événements 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++) {
// Boucle pour simuler du travail.
}
} else {
// Cette sortie sera bloquée par la boucle for dans le thread principal.
console.log('foo')
}
'use strict'
const { Worker, isMainThread } = require('node:worker_threads')
if (isMainThread) {
new Worker(__filename)
for (let n = 0; n < 1e10; n++) {
// Boucle pour simuler du travail.
}
} else {
// Cette sortie sera bloquée par la boucle for dans le thread principal.
console.log('foo')
}
Lancement de threads worker à partir de scripts de préchargement
Soyez prudent lorsque vous lancez des threads worker à partir de scripts de préchargement (scripts chargés et exécutés à l'aide de l'indicateur de ligne de commande -r
). À moins que l'option execArgv
ne soit explicitement définie, les nouveaux threads Worker héritent automatiquement des indicateurs de ligne de commande du processus en cours d'exécution et préchargeront les mêmes scripts de préchargement que le thread principal. Si le script de préchargement lance inconditionnellement un thread worker, chaque thread généré en engendrera un autre jusqu'à ce que l'application plante.