ワーカートレッド
ソースコード: lib/worker_threads.js
node:worker_threads
モジュールは、JavaScript を並列実行するスレッドの使用を可能にします。アクセスするには:
const worker = require('node:worker_threads')
ワーカー(スレッド)は、CPU 集約的な JavaScript 操作を実行するのに役立ちます。I/O 集約的な作業にはあまり役立ちません。Node.js 組み込みの非同期 I/O 操作は、ワーカーよりも効率的です。
child_process
やcluster
とは異なり、worker_threads
はメモリを共有できます。ArrayBuffer
インスタンスを転送するか、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))
}
上記の例では、各parseJSAsync()
呼び出しに対してワーカースレッドを生成します。実際には、このようなタスクにはワーカーのプールを使用します。そうでなければ、ワーカーの作成に伴うオーバーヘッドが、そのメリットを上回る可能性があります。
ワーカープールを実装する際には、AsyncResource
API を使用して、診断ツールにタスクとその結果の相関関係を知らせます(たとえば、非同期スタックトレースを提供するため)。例として、async_hooks
ドキュメントの"Using AsyncResource
for a Worker
thread pool"を参照してください。
ワーカースレッドは、デフォルトでプロセス固有ではないオプションを継承します。ワーカースレッドのオプション、特にargv
とexecArgv
オプションをカスタマイズする方法については、Worker constructor options
を参照してください。
worker.getEnvironmentData(key)
[履歴]
バージョン | 変更 |
---|---|
v17.5.0, v16.15.0 | 実験的ではなくなりました。 |
v15.12.0, v14.18.0 | 追加: v15.12.0, v14.18.0 |
ワーカスレッド内で、worker.getEnvironmentData()
は、生成スレッドの worker.setEnvironmentData()
に渡されたデータのクローンを返します。新しい Worker
はそれぞれ、環境データの独自の複製を自動的に受け取ります。
const { Worker, isMainThread, setEnvironmentData, getEnvironmentData } = require('node:worker_threads')
if (isMainThread) {
setEnvironmentData('Hello', 'World!')
const worker = new Worker(__filename)
} else {
console.log(getEnvironmentData('Hello')) // 'World!' を出力します。
}
worker.isMainThread
追加: v10.5.0
このコードが Worker
スレッド内で実行されていない場合、true
になります。
const { Worker, isMainThread } = require('node:worker_threads')
if (isMainThread) {
// これは、Worker インスタンス内で現在のファイルを読み込み直します。
new Worker(__filename)
} else {
console.log('Inside Worker!')
console.log(isMainThread) // 'false' を出力します。
}
worker.markAsUntransferable(object)
追加: v14.5.0, v12.19.0
object
<any> 任意の JavaScript 値。
オブジェクトを転送不可としてマークします。object
が port.postMessage()
コールの転送リストに含まれている場合、エラーがスローされます。object
がプリミティブ値の場合、これはノーオプです。
特に、これは、転送するのではなくクローンできるオブジェクト、および送信側で他のオブジェクトによって使用されるオブジェクトにとって理にかなっています。たとえば、Node.js は、Buffer
プール に使用する ArrayBuffer
をこれを使ってマークします。
この操作は元に戻すことができません。
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 {
// これはエラーをスローします。pooledBuffer は転送不可だからです。
port1.postMessage(typedArray1, [typedArray1.buffer])
} catch (error) {
// error.name === 'DataCloneError'
}
// 次の行は typedArray1 の内容を出力します -- これはまだメモリを所有しており、転送されていません。
// `markAsUntransferable()` がなければ、これは空の Uint8Array を出力し、postMessage コールは成功していました。
// typedArray2 も無傷です。
console.log(typedArray1)
console.log(typedArray2)
ブラウザにはこの API に相当するものはありません。
worker.isMarkedAsUntransferable(object)
追加:v21.0.0
markAsUntransferable()
で転送不可としてマークされているかどうかをチェックします。
const { markAsUntransferable, isMarkedAsUntransferable } = require('node:worker_threads')
const pooledBuffer = new ArrayBuffer(8)
markAsUntransferable(pooledBuffer)
isMarkedAsUntransferable(pooledBuffer) // trueを返します。
ブラウザにはこの API に相当するものはありません。
worker.markAsUncloneable(object)
追加:v23.0.0
object
<any> 任意の JavaScript 値。
オブジェクトを複製不可としてマークします。object
がmessage
としてport.postMessage()
呼び出しで使用された場合、エラーがスローされます。object
がプリミティブ値の場合、これはノーオペレーションです。
これはArrayBuffer
やBuffer
のようなオブジェクトには影響しません。
この操作は元に戻せません。
const { markAsUncloneable } = require('node:worker_threads')
const anyObject = { foo: 'bar' }
markAsUncloneable(anyObject)
const { port1 } = new MessageChannel()
try {
// anyObjectは複製不可なので、これはエラーをスローします。
port1.postMessage(anyObject)
} catch (error) {
// error.name === 'DataCloneError'
}
ブラウザにはこの API に相当するものはありません。
worker.moveMessagePortToContext(port, contextifiedSandbox)
追加:v11.13.0
port
<MessagePort> 転送するメッセージポート。contextifiedSandbox
<Object>vm.createContext()
メソッドによって返される、コンテキスト化されたオブジェクト。- 戻り値: <MessagePort>
MessagePort
を異なるvm
コンテキストに転送します。元のport
オブジェクトは使用できなくなり、返されたMessagePort
インスタンスがその代わりになります。
返されたMessagePort
はターゲットコンテキスト内のオブジェクトであり、そのグローバルObject
クラスを継承します。port.onmessage()
リスナーに渡されるオブジェクトもターゲットコンテキストで作成され、そのグローバルObject
クラスを継承します。
ただし、作成されたMessagePort
はEventTarget
を継承しなくなり、port.onmessage()
のみを使用してイベントを受信できます。
worker.parentPort
追加日時: v10.5.0
このスレッドがWorker
である場合、これは親スレッドとの通信を可能にするMessagePort
です。parentPort.postMessage()
を使用して送信されたメッセージは、worker.on('message')
を使用して親スレッドで使用でき、worker.postMessage()
を使用して親スレッドから送信されたメッセージは、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) // 'Hello, world!'と出力します。
})
worker.postMessage('Hello, world!')
} else {
// 親スレッドからのメッセージを受信したら、それを送り返します。
parentPort.once('message', message => {
parentPort.postMessage(message)
})
}
worker.postMessageToThread(threadId, value[, transferList][, timeout])
追加日時: v22.5.0
threadId
<number> ターゲットスレッドの ID。スレッド ID が無効な場合、ERR_WORKER_MESSAGING_FAILED
エラーがスローされます。ターゲットスレッド ID が現在のスレッド ID の場合、ERR_WORKER_MESSAGING_SAME_THREAD
エラーがスローされます。value
<any> 送信する値。transferList
<Object[]>value
に 1 つ以上のMessagePort
のようなオブジェクトが渡された場合、それらのアイテムにはtransferList
が必要です。そうでない場合、ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
がスローされます。詳細はport.postMessage()
を参照してください。timeout
<number> メッセージが配信されるまで待機する時間(ミリ秒単位)。デフォルトはundefined
で、永遠に待機することを意味します。操作がタイムアウトした場合、ERR_WORKER_MESSAGING_TIMEOUT
エラーがスローされます。- 戻り値: <Promise> メッセージが宛先スレッドによって正常に処理された場合に解決される Promise。
スレッド ID で識別される別のワーカーに値を送信します。
ターゲットスレッドにworkerMessage
イベントのリスナーがない場合、ERR_WORKER_MESSAGING_FAILED
エラーがスローされます。
ターゲットスレッドがworkerMessage
イベントの処理中にエラーをスローした場合、ERR_WORKER_MESSAGING_ERRORED
エラーがスローされます。
このメソッドは、ターゲットスレッドが現在のスレッドの直接の親または子ではない場合に使用してください。2 つのスレッドが親子関係にある場合は、require('node:worker_threads').parentPort.postMessage()
とworker.postMessage()
を使用してスレッド間の通信を行ってください。
次の例は、postMessageToThread
の使用を示しています。10 個のネストされたスレッドを作成し、最後の子スレッドがメインスレッドと通信しようとします。
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)
[履歴]
バージョン | 変更 |
---|---|
v15.12.0 | port 引数は、BroadcastChannel も参照できるようになりました。 |
v12.3.0 | 追加: v12.3.0 |
port
<MessagePort> | <BroadcastChannel>- 戻り値: <Object> | <undefined>
指定されたMessagePort
から単一のメッセージを受信します。メッセージが利用できない場合はundefined
を返し、それ以外の場合はMessagePort
のキュー内の最も古いメッセージを含むメッセージペイロードを含む単一のmessage
プロパティを持つオブジェクトを返します。
const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
port1.postMessage({ hello: 'world' })
console.log(receiveMessageOnPort(port2))
// 出力: { message: { hello: 'world' } }
console.log(receiveMessageOnPort(port2))
// 出力: undefined
この関数が使用されると、'message'
イベントは発生せず、onmessage
リスナーは呼び出されません。
worker.resourceLimits
追加: v13.2.0, v12.16.0
このワーカー・スレッド内の JS エンジンのリソース制約のセットを提供します。resourceLimits
オプションがWorker
コンストラクタに渡された場合、これはその値と一致します。
メインスレッドで使用した場合、その値は空のオブジェクトです。
worker.SHARE_ENV
追加日時: v11.14.0
Worker
コンストラクタのenv
オプションに渡すことができる特別な値で、現在のスレッドとワーカースレッドが同じ環境変数セットへの読み書きアクセスを共有することを示します。
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) // 'foo'を出力します。
})
worker.setEnvironmentData(key[, value])
[履歴]
バージョン | 変更 |
---|---|
v17.5.0, v16.15.0 | 実験的な機能ではなくなりました。 |
v15.12.0, v14.18.0 | 追加日時: v15.12.0, v14.18.0 |
key
<any> <Map>のキーとして使用できる、任意のクローン可能な JavaScript 値。value
<any> クローンされて、新しいすべてのWorker
インスタンスに自動的に渡される任意のクローン可能な JavaScript 値。value
がundefined
として渡された場合、key
に対して以前に設定された値は削除されます。
worker.setEnvironmentData()
API は、現在のスレッドと現在のコンテキストから生成されたすべての新しいWorker
インスタンスにおいて、worker.getEnvironmentData()
の内容を設定します。
worker.threadId
追加日時: v10.5.0
現在のスレッドの整数識別子。対応するワーカーオブジェクト(存在する場合)では、worker.threadId
として利用できます。この値は、単一のプロセス内の各Worker
インスタンスに対して一意です。
worker.workerData
追加版:v10.5.0
このスレッドの Worker
コンストラクタに渡されたデータのクローンを含む、任意の JavaScript 値。
データは、HTML 構造化クローンアルゴリズムに従って、postMessage()
を使用した場合と同じようにクローンされます。
const { Worker, isMainThread, workerData } = require('node:worker_threads')
if (isMainThread) {
const worker = new Worker(__filename, { workerData: 'Hello, world!' })
} else {
console.log(workerData) // 'Hello, world!' と出力します。
}
クラス: BroadcastChannel extends EventTarget
[履歴]
バージョン | 変更点 |
---|---|
v18.0.0 | 実験的な段階ではなくなりました。 |
v15.4.0 | 追加版:v15.4.0 |
BroadcastChannel
のインスタンスは、同じチャネル名にバインドされている他のすべての BroadcastChannel
インスタンスとの非同期の一対多の通信を可能にします。
'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)
追加版:v15.4.0
name
<any> 接続するチャネルの名前。${name}
を使用して文字列に変換できる任意の JavaScript 値が許可されます。
broadcastChannel.close()
追加版:v15.4.0
BroadcastChannel
接続を閉じます。
broadcastChannel.onmessage
追加版:v15.4.0
- 型: <Function> メッセージを受信したときに、単一の
MessageEvent
引数で呼び出されます。
broadcastChannel.onmessageerror
追加されたバージョン: v15.4.0
- 型: <Function> 受信したメッセージがデシリアライズできない場合に呼び出されます。
broadcastChannel.postMessage(message)
追加されたバージョン: v15.4.0
message
<any> クローン可能な任意の JavaScript 値。
broadcastChannel.ref()
追加されたバージョン: v15.4.0
unref()
の反対です。以前unref()
された BroadcastChannel に対してref()
を呼び出しても、それが唯一のアクティブなハンドルである場合でもプログラムが終了することはありません(デフォルトの動作)。ポートがref()
されている場合、ref()
を再度呼び出しても効果はありません。
broadcastChannel.unref()
追加されたバージョン: v15.4.0
BroadcastChannel に対してunref()
を呼び出すと、それがイベントシステムで唯一のアクティブなハンドルである場合、スレッドを終了できます。BroadcastChannel が既にunref()
されている場合、unref()
を再度呼び出しても効果はありません。
クラス: MessageChannel
追加されたバージョン: v10.5.0
worker.MessageChannel
クラスのインスタンスは、非同期で双方向の通信チャネルを表します。MessageChannel
自体にはメソッドがありません。new MessageChannel()
は、リンクされたMessagePort
インスタンスを参照するport1
とport2
プロパティを持つオブジェクトを生成します。
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
port1.on('message', message => console.log('received', message))
port2.postMessage({ foo: 'bar' })
// `port1.on('message')`リスナーから:received { foo: 'bar' } と出力されます
クラス: MessagePort
[履歴]
バージョン | 変更 |
---|---|
v14.7.0 | このクラスは、EventEmitter ではなくEventTarget を継承するようになりました。 |
v10.5.0 | 追加されたバージョン: v10.5.0 |
- 拡張: <EventTarget>
worker.MessagePort
クラスのインスタンスは、非同期で双方向の通信チャネルの一端を表します。異なるWorker
間で構造化データ、メモリ領域、その他のMessagePort
を転送するために使用できます。
この実装は、ブラウザのMessagePort
と一致します。
イベント: 'close'
追加バージョン: v10.5.0
'close'
イベントは、チャネルのどちらかの側が切断されたときに一度発生します。
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
// 出力:
// foobar
// closed!
port2.on('message', message => console.log(message))
port2.on('close', () => console.log('closed!'))
port1.postMessage('foobar')
port1.close()
イベント: 'message'
追加バージョン: v10.5.0
value
<any> 送信された値
'message'
イベントは、受信したメッセージごとに発生し、port.postMessage()
のクローンされた入力を含みます。
このイベントのリスナーは、postMessage()
に渡された value
パラメーターのクローンと、それ以上の引数を受け取ります。
イベント: 'messageerror'
追加バージョン: v14.5.0, v12.19.0
error
<Error> Error オブジェクト
'messageerror'
イベントは、メッセージの逆シリアル化が失敗したときに発生します。
現在、このイベントは、受信側で投稿された JS オブジェクトをインスタンス化中にエラーが発生した場合に発生します。このような状況はまれですが、たとえば、特定の Node.js API オブジェクトが vm.Context
(Node.js API が現在使用できない場所) で受信された場合に発生する可能性があります。
port.close()
追加バージョン: v10.5.0
接続のどちらかの側でのメッセージの送信を無効にします。このメソッドは、この MessagePort
を介してこれ以上の通信が行われない場合に呼び出すことができます。
'close'
イベント は、チャネルの一部である両方の MessagePort
インスタンスで発生します。
port.postMessage(value[, transferList])
[履歴]
バージョン | 変更 |
---|---|
v21.0.0 | 転送できないオブジェクトが転送リストにある場合、エラーがスローされます。 |
v15.6.0 | クローン可能な型の一覧に X509Certificate を追加しました。 |
v15.0.0 | クローン可能な型の一覧に CryptoKey を追加しました。 |
v15.14.0, v14.18.0 | クローン可能な型の一覧に 'BlockList' を追加しました。 |
v15.9.0, v14.18.0 | クローン可能な型の一覧に 'Histogram' 型を追加しました。 |
v14.5.0, v12.19.0 | クローン可能な型の一覧に KeyObject を追加しました。 |
v14.5.0, v12.19.0 | 転送可能な型の一覧に FileHandle を追加しました。 |
v10.5.0 | 追加バージョン: v10.5.0 |
value
<any>transferList
<Object[]>
このチャネルの受信側に JavaScript の値を送信します。value
は、HTML 構造化クローンアルゴリズム と互換性のある方法で転送されます。
特に、JSON
との重要な違いは次のとおりです。
value
には循環参照を含めることができます。value
には、RegExp
、BigInt
、Map
、Set
など、組み込みの JS 型のインスタンスを含めることができます。value
には、ArrayBuffer
とSharedArrayBuffer
の両方を使用する型付き配列を含めることができます。value
にはWebAssembly.Module
インスタンスを含めることができます。value
には、次のもの以外のネイティブ (C++ バックエンド) オブジェクトを含めることはできません。
const { MessageChannel } = require('node:worker_threads')
const { port1, port2 } = new MessageChannel()
port1.on('message', message => console.log(message))
const circularData = {}
circularData.foo = circularData
// 出力: { foo: [Circular] }
port2.postMessage(circularData)
transferList
は、ArrayBuffer
、MessagePort
、および FileHandle
オブジェクトのリストにすることができます。転送後、それらはチャネルの送信側では使用できなくなります ( value
に含まれていない場合でも)。子プロセス とは異なり、ネットワークソケットなどのハンドルの転送は現在サポートされていません。
value
に SharedArrayBuffer
インスタンスが含まれている場合、それらはどちらのスレッドからもアクセスできます。それらを transferList
にリストすることはできません。
value
には、transferList
にない ArrayBuffer
インスタンスが含まれている可能性があります。その場合、基礎となるメモリは移動されるのではなくコピーされます。
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])
// これは `uint8Array` のコピーを投稿します。
port2.postMessage(uint8Array)
// これはデータをコピーしませんが、`uint8Array` を使用できなくします。
port2.postMessage(uint8Array, [uint8Array.buffer])
// `sharedUint8Array` のメモリは、元のメモリと `.on('message')` で受信したコピーの両方からアクセスできます。
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4))
port2.postMessage(sharedUint8Array)
// これは新しく作成されたメッセージポートを受信側に転送します。
// これは、たとえば、同じ親スレッドの子である複数の `Worker` スレッド間に通信チャネルを作成するために使用できます。
const otherChannel = new MessageChannel()
port2.postMessage({ port: otherChannel.port1 }, [otherChannel.port1])
メッセージオブジェクトはすぐにクローンされ、投稿後に副作用なしで変更できます。
この API の背後にあるシリアル化と逆シリアル化メカニズムの詳細については、node:v8
モジュールのシリアル化 API を参照してください。
TypedArray と Buffer の転送時の考慮事項
全てのTypedArray
とBuffer
インスタンスは、基となるArrayBuffer
へのビューです。つまり、生のデータを実際に格納するのはArrayBuffer
であり、TypedArray
とBuffer
オブジェクトはデータの表示と操作の方法を提供します。同じArrayBuffer
インスタンスに対して複数のビューを作成することは可能であり、一般的です。転送リストを使用してArrayBuffer
を転送する際には細心の注意を払う必要があります。そうすることで、その同じArrayBuffer
を共有する全てのTypedArray
とBuffer
インスタンスが使用できなくなってしまうためです。
const ab = new ArrayBuffer(10)
const u1 = new Uint8Array(ab)
const u2 = new Uint16Array(ab)
console.log(u2.length) // 5と出力
port.postMessage(u1, [u1.buffer])
console.log(u2.length) // 0と出力
Buffer
インスタンスについては、特に、基となるArrayBuffer
を転送できるか、クローンできるかは、インスタンスの作成方法に完全に依存しており、多くの場合、確実に判断することはできません。
ArrayBuffer
は、markAsUntransferable()
を使用して、常にクローンされ、決して転送されないようにマークできます。
Buffer
インスタンスの作成方法によっては、基となるArrayBuffer
を所有している場合と所有していない場合があります。Buffer
インスタンスがArrayBuffer
を所有していることが分かっている場合を除き、ArrayBuffer
を転送することはできません。特に、内部Buffer
プールから作成されたBuffer
(例えばBuffer.from()
またはBuffer.allocUnsafe()
を使用)の場合、それらを転送することは不可能であり、常にクローンされます。これにより、Buffer
プールの全体のコピーが送信されます。この動作は、意図しないメモリ使用量の増加と、潜在的なセキュリティ上の問題を伴う可能性があります。
Buffer
プーリングの詳細については、Buffer.allocUnsafe()
を参照してください。
Buffer.alloc()
またはBuffer.allocUnsafeSlow()
を使用して作成されたBuffer
インスタンスのArrayBuffer
は常に転送できますが、そうすると、それらのArrayBuffer
の他の既存のビューは使用できなくなります。
プロトタイプ、クラス、アクセッサを持つオブジェクトを複製する場合の考慮事項
オブジェクトの複製はHTML 構造化複製アルゴリズムを使用するため、非列挙可能なプロパティ、プロパティアクセッサ、オブジェクトのプロトタイプは保持されません。特に、Buffer
オブジェクトは受信側ではプレーンのUint8Array
として読み取られ、JavaScript クラスのインスタンスはプレーンの 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 }
この制限は、グローバルなURL
オブジェクトなどの多くの組み込みオブジェクトにも適用されます。
const { port1, port2 } = new MessageChannel()
port1.onmessage = ({ data }) => console.log(data)
port2.postMessage(new URL('https://example.org'))
// Prints: { }
port.hasRef()
追加されたバージョン: v18.1.0, v16.17.0
- 戻り値: <boolean>
true
の場合、MessagePort
オブジェクトは Node.js イベントループをアクティブな状態に保ちます。
port.ref()
追加されたバージョン: v10.5.0
unref()
の反対です。以前にunref()
されたポートに対してref()
を呼び出しても、それが唯一のアクティブなハンドルである場合でもプログラムが終了することはありません(デフォルトの動作)。ポートがref()
されている場合、ref()
をもう一度呼び出しても効果はありません。
リスナーが.on('message')
を使用してアタッチまたは削除された場合、イベントのリスナーが存在するかどうかによって、ポートは自動的にref()
およびunref()
されます。
port.start()
追加日時: v10.5.0
このMessagePort
でメッセージの受信を開始します。このポートをイベントエミッタとして使用する場合、'message'
リスナーがアタッチされると自動的に呼び出されます。
このメソッドは、Web のMessagePort
API との互換性のために存在します。Node.js では、イベントリスナーが存在しない場合にメッセージを無視する場合にのみ役立ちます。Node.js は.onmessage
の処理においても異なります。これを設定すると自動的に.start()
が呼び出されますが、設定解除すると、新しいハンドラーが設定されるか、ポートが破棄されるまでメッセージがキューに蓄積されます。
port.unref()
追加日時: v10.5.0
ポートでunref()
を呼び出すと、これがイベントシステム内の唯一のアクティブなハンドルである場合、スレッドを終了できます。ポートが既にunref()
されている場合、unref()
を再度呼び出しても効果はありません。
.on('message')
を使用してリスナーがアタッチまたは削除されると、イベントのリスナーが存在するかどうかによって、ポートは自動的にref()
およびunref()
されます。
クラス: Worker
追加日時: v10.5.0
- 拡張元: <EventEmitter>
Worker
クラスは、独立した JavaScript 実行スレッドを表します。ほとんどの Node.js API は内部で使用できます。
Worker 環境内の注目すべき違いは次のとおりです。
process.stdin
、process.stdout
、およびprocess.stderr
ストリームは、親スレッドによってリダイレクトされる可能性があります。require('node:worker_threads').isMainThread
プロパティはfalse
に設定されます。require('node:worker_threads').parentPort
メッセージポートが使用可能です。process.exit()
はプログラム全体を停止するのではなく、単一のスレッドのみを停止し、process.abort()
は使用できません。process.chdir()
およびグループ ID またはユーザー ID を設定するprocess
メソッドは使用できません。process.env
は、特に指定がない限り、親スレッドの環境変数の複製です。一方の複製への変更は、他のスレッドでは見えませんし、ネイティブアドオンにも見えません(worker.SHARE_ENV
がWorker
コンストラクタのenv
オプションとして渡されない限り)。Windows では、メインスレッドとは異なり、環境変数のコピーは大文字と小文字を区別して動作します。process.title
を変更することはできません。- シグナルは
process.on('...')
を介して配信されません。 worker.terminate()
が呼び出された結果、実行が任意の時点で停止することがあります。- 親プロセスからの IPC チャネルにはアクセスできません。
trace_events
モジュールはサポートされていません。- ネイティブアドオンは、特定の条件を満たしている場合にのみ、複数のスレッドからロードできます。
他のWorker
内でWorker
インスタンスを作成することは可能です。
Web Workersやnode:cluster
モジュールと同様に、スレッド間のメッセージパッシングを通じて双方向通信を実現できます。内部的に、Worker
はWorker
が作成された時点で既に互いに関連付けられている、組み込みのMessagePort
のペアを持っています。親側のMessagePort
オブジェクトは直接公開されていませんが、その機能はworker.postMessage()
と親スレッドのWorker
オブジェクトのworker.on('message')
イベントを介して公開されています。
カスタムメッセージングチャネルを作成するには(懸念事項の分離を容易にするため、デフォルトのグローバルチャネルの使用よりも推奨されます)、どちらのスレッドでもMessageChannel
オブジェクトを作成し、そのMessageChannel
のMessagePort
の 1 つを、グローバルチャネルなど、既存のチャネルを介して他のスレッドに渡すことができます。
メッセージの渡し方、およびスレッドの障壁を介して正常に転送できる JavaScript 値の種類の詳細については、port.postMessage()
を参照してください。
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])
[履歴]
バージョン | 変更点 |
---|---|
v19.8.0, v18.16.0 | デバッグのためにワーカーのタイトルに名前を追加できるname オプションのサポートを追加しました。 |
v14.9.0 | filename パラメータは、data: プロトコルを使用する WHATWG URL オブジェクトを使用できます。 |
v14.9.0 | trackUnmanagedFds オプションはデフォルトでtrue に設定されました。 |
v14.6.0, v12.19.0 | trackUnmanagedFds オプションが導入されました。 |
v13.13.0, v12.17.0 | transferList オプションが導入されました。 |
v13.12.0, v12.17.0 | filename パラメータは、file: プロトコルを使用する WHATWG URL オブジェクトを使用できます。 |
v13.4.0, v12.16.0 | argv オプションが導入されました。 |
v13.2.0, v12.16.0 | resourceLimits オプションが導入されました。 |
v10.5.0 | 追加: v10.5.0 |
filename
<string> | <URL> ワーカーのメインスクリプトまたはモジュールのパス。絶対パスまたは./
または../
で始まる相対パス(つまり、現在の作業ディレクトリを基準とした相対パス)、またはfile:
またはdata:
プロトコルを使用する WHATWGURL
オブジェクトのいずれかでなければなりません。data:
URLを使用する場合、データは MIME タイプに基づいてECMAScript モジュールローダーを使用して解釈されます。options.eval
がtrue
の場合、これはパスではなく JavaScript コードを含む文字列です。options
<Object>argv
<any[]> ワーカーのprocess.argv
に追加される文字列に変換される引数のリスト。これは主にworkerData
に似ていますが、CLI オプションとしてスクリプトに渡されたかのように、グローバルなprocess.argv
で値を利用できます。env
<Object> 設定されている場合、ワーカスレッド内のprocess.env
の初期値を指定します。特殊な値として、worker.SHARE_ENV
を使用して、親スレッドと子スレッドが環境変数を共有する必要があることを指定できます。その場合、一方のスレッドのprocess.env
オブジェクトへの変更は、もう一方のスレッドにも影響します。デフォルト:process.env
。eval
<boolean>true
の場合、最初の引数がstring
であり、コンストラクタへの最初の引数を、ワーカーがオンラインになったときに実行されるスクリプトとして解釈します。execArgv
<string[]> ワーカーに渡されるノード CLI オプションのリスト。V8 オプション(--max-old-space-size
など)とプロセスに影響を与えるオプション(--title
など)はサポートされていません。設定されている場合、これはワーカー内でprocess.execArgv
として提供されます。デフォルトでは、オプションは親スレッドから継承されます。stdin
<boolean> これがtrue
に設定されている場合、worker.stdin
は、その内容がワーカー内のprocess.stdin
として表示される書き込み可能なストリームを提供します。デフォルトでは、データは提供されません。stdout
<boolean> これがtrue
に設定されている場合、worker.stdout
は親のprocess.stdout
に自動的にパイプされません。stderr
<boolean> これがtrue
に設定されている場合、worker.stderr
は親のprocess.stderr
に自動的にパイプされません。workerData
<any> クローンされ、require('node:worker_threads').workerData
として利用可能になる任意の JavaScript 値。クローンはHTML 構造化クローンアルゴリズムで説明されているように発生し、オブジェクトをクローンできない場合(たとえば、function
が含まれている場合)、エラーがスローされます。trackUnmanagedFds
<boolean> これがtrue
に設定されている場合、ワーカーはfs.open()
とfs.close()
を介して管理される生のファイルディスクリプターを追跡し、ワーカーが終了するときに、ネットワークソケットやFileHandle
API を介して管理されるファイルディスクリプターなどの他のリソースと同様に、それらを閉じます。このオプションは、すべてのネストされたWorker
によって自動的に継承されます。デフォルト:true
。transferList
<Object[]>workerData
に 1 つ以上のMessagePort
のようなオブジェクトが渡された場合、これらのアイテムにはtransferList
が必要です。そうでない場合、ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
がスローされます。詳細については、port.postMessage()
を参照してください。resourceLimits
<Object> 新しい JS エンジンインスタンスのリソース制限のオプションセット。これらの制限に達すると、Worker
インスタンスが終了します。これらの制限は JS エンジンにのみ影響し、外部データ(ArrayBuffer
を含まない)には影響しません。これらの制限が設定されている場合でも、グローバルなメモリ不足が発生した場合、プロセスは依然として異常終了する可能性があります。maxOldGenerationSizeMb
<number> メインヒープの最大サイズ(MB 単位)。コマンドライン引数--max-old-space-size
が設定されている場合、この設定よりも優先されます。maxYoungGenerationSizeMb
<number> 最近作成されたオブジェクトのヒープスペースの最大サイズ。コマンドライン引数--max-semi-space-size
が設定されている場合、この設定よりも優先されます。codeRangeSizeMb
<number> 生成されたコードに使用される事前に割り当てられたメモリ範囲のサイズ。stackSizeMb
<number> スレッドのデフォルトの最大スタックサイズ。小さい値では、使用できないワーカーインスタンスが発生する可能性があります。デフォルト:4
。name
<string> デバッグ/識別目的でワーカーのタイトルに追加されるオプションのname
。最終的なタイトルは[worker ${id}] ${name}
となります。デフォルト:''
。
イベント: 'error'
追加日時: v10.5.0
err
<Error>
'error'
イベントは、ワーカスレッドがキャッチされない例外をスローした場合に発生します。その場合、ワーカーは終了します。
イベント: 'exit'
追加日時: v10.5.0
exitCode
<整数>
'exit'
イベントは、ワーカーが停止した後に発生します。ワーカーがprocess.exit()
を呼び出して終了した場合、exitCode
パラメーターは渡された終了コードになります。ワーカーが強制終了された場合、exitCode
パラメーターは1
になります。
これは、任意のWorker
インスタンスによって発生する最後のイベントです。
イベント: 'message'
追加日時: v10.5.0
value
<任意> 送信された値
'message'
イベントは、ワーカスレッドがrequire('node:worker_threads').parentPort.postMessage()
を呼び出したときに発生します。詳細はport.on('message')
イベントを参照してください。
ワーカスレッドから送信されたすべてのメッセージは、Worker
オブジェクトで'exit'
イベントが発生する前に発生します。
イベント: 'messageerror'
追加日時: v14.5.0, v12.19.0
error
<Error> Error オブジェクト
'messageerror'
イベントは、メッセージの逆シリアル化に失敗した場合に発生します。
イベント: 'online'
追加日時: v10.5.0
'online'
イベントは、ワーカスレッドが JavaScript コードの実行を開始したときに発生します。
worker.getHeapSnapshot([options])
[履歴]
バージョン | 変更 |
---|---|
v19.1.0 | ヒープスナップショットの設定オプションをサポート |
v13.9.0, v12.17.0 | 追加日時: v13.9.0, v12.17.0 |
ワーカーの現在の状態の V8 スナップショット用の読み取り可能なストリームを返します。詳細はv8.getHeapSnapshot()
を参照してください。
'exit'
イベントが発生する前にワーカスレッドが実行されなくなると、返されるPromise
はERR_WORKER_NOT_RUNNING
エラーで直ちに拒否されます。
worker.performance
追加日時: v15.1.0, v14.17.0, v12.22.0
ワーカーインスタンスからパフォーマンス情報を照会するために使用できるオブジェクトです。perf_hooks.performance
に似ています。
performance.eventLoopUtilization([utilization1[, utilization2]])
追加日時: v15.1.0, v14.17.0, v12.22.0
utilization1
<Object>eventLoopUtilization()
への以前の呼び出しの結果。utilization2
<Object>utilization1
より前のeventLoopUtilization()
への以前の呼び出しの結果。- 戻り値: <Object>
perf_hooks
eventLoopUtilization()
と同じ呼び出しですが、ワーカーインスタンスの値が返されます。
1 つの違いは、メインスレッドとは異なり、ワーカー内のブートストラップはイベントループ内で行われることです。そのため、ワーカーのスクリプトの実行が開始されると、イベントループの使用率はすぐに利用できます。
idle
時間が増加しないことは、ワーカーがブートストラップでスタックしていることを意味するわけではありません。次の例は、ワーカーのライフタイム全体でidle
時間が蓄積されないにもかかわらず、メッセージを処理できることを示しています。
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)
ワーカーのイベントループの使用率は、'online'
イベント の発生後でのみ利用でき、それより前、または 'exit'
イベント の後であれば、すべてのプロパティの値は 0
になります。
worker.postMessage(value[, transferList])
追加日時: v10.5.0
value
<any>transferList
<Object[]>
require('node:worker_threads').parentPort.on('message')
を介して受信されるワーカーにメッセージを送信します。詳細はport.postMessage()
を参照してください。
worker.ref()
追加日時: v10.5.0
unref()
の反対です。以前にunref()
されたワーカーでref()
を呼び出しても、それが唯一のアクティブなハンドルが残っている場合でも、プログラムが終了することはありません(デフォルトの動作)。ワーカーがref()
されている場合、ref()
を再度呼び出しても効果はありません。
worker.resourceLimits
追加日時: v13.2.0, v12.16.0
このワーカスレッドに対する JS エンジンのリソース制約のセットを提供します。resourceLimits
オプションがWorker
コンストラクタに渡された場合、これはその値と一致します。
ワーカーが停止した場合、戻り値は空のオブジェクトになります。
worker.stderr
追加日時: v10.5.0
これは、ワーカスレッド内でprocess.stderr
に書き込まれたデータを含む読み取り可能なストリームです。stderr: true
がWorker
コンストラクタに渡されなかった場合、データは親スレッドのprocess.stderr
ストリームにパイプされます。
worker.stdin
追加されたバージョン: v10.5.0
stdin: true
をWorker
コンストラクタに渡した場合、これは書き込み可能なストリームになります。このストリームに書き込まれたデータは、ワーカスレッドでprocess.stdin
として利用可能になります。
worker.stdout
追加されたバージョン: v10.5.0
これは、ワーカスレッド内でprocess.stdout
に書き込まれたデータを含む読み取り可能なストリームです。stdout: true
がWorker
コンストラクタに渡されなかった場合、データは親スレッドのprocess.stdout
ストリームにパイプされます。
worker.terminate()
[履歴]
バージョン | 変更点 |
---|---|
v12.5.0 | この関数は Promise を返すようになりました。コールバックの渡しが非推奨となり、このバージョンまでは実際にはワーカーは同期的に終了していたため、役に立ちませんでした。終了は完全に非同期操作になりました。 |
v10.5.0 | 追加されました: v10.5.0 |
- 戻り値: <Promise>
ワーカスレッドでのすべての JavaScript 実行をできるだけ早く停止します。'exit'
イベントが発行されたときに解決される、終了コードの Promise を返します。
worker.threadId
追加されたバージョン: v10.5.0
参照されるスレッドの整数識別子です。ワーカスレッド内では、require('node:worker_threads').threadId
として利用できます。この値は、単一のプロセス内の各Worker
インスタンスに対して一意です。
worker.unref()
追加されたバージョン: v10.5.0
ワーカーでunref()
を呼び出すと、それがイベントシステム内の唯一のアクティブなハンドルである場合、スレッドを終了できます。ワーカーが既にunref()
されている場合、unref()
を再度呼び出しても効果はありません。
メモ
stdio の同期ブロッキング
Worker
は、<MessagePort>
を介したメッセージパッシングを使用してstdio
とのやり取りを実装します。これは、Worker
から発信されるstdio
出力は、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++) {
// ループで作業をシミュレートします。
}
} else {
// この出力はメインスレッドのforループによってブロックされます。
console.log('foo')
}
'use strict'
const { Worker, isMainThread } = require('node:worker_threads')
if (isMainThread) {
new Worker(__filename)
for (let n = 0; n < 1e10; n++) {
// ループで作業をシミュレートします。
}
} else {
// この出力はメインスレッドのforループによってブロックされます。
console.log('foo')
}
プリロードスクリプトからのワーカスレッドの起動
プリロードスクリプト(-r
コマンドラインフラグを使用してロードおよび実行されるスクリプト)からワーカスレッドを起動する場合に注意が必要です。execArgv
オプションを明示的に設定しない限り、新しいワーカスレッドは実行中のプロセスからコマンドラインフラグを自動的に継承し、メインスレッドと同じプリロードスクリプトをプリロードします。プリロードスクリプトがワーカスレッドを無条件に起動する場合、生成されたすべてのスレッドがさらに別のスレッドを生成し、アプリケーションがクラッシュするまで続きます。