限界突破!Node.jsで並行処理を極める:spawn、fork、worker_threadsを組み合わせた高速化テクニック
Node.jsにおける「spawn」と「fork」の違い:子プロセス詳解
生成方法
- fork: 現在のNode.jsプロセスを複製し、新しい子プロセスとして実行します。複製されたプロセスは、親プロセスと同じJavaScript環境で動作します。
- spawn: 別のプログラム(シェルコマンドを含む)を新しいプロセスとして実行します。
メモリ共有
- fork: 親プロセスと子プロセスは、メモリ空間の一部を共有します。そのため、データ共有がより効率的に行えます。
- spawn: 親プロセスと子プロセスは別々のメモリ空間を持ちます。そのため、データ共有にはIPC(Inter-Process Communication)と呼ばれるメッセージング機構が必要です。
処理速度
- fork: メモリ共有とコンテキストスイッチの省略により、spawnよりも処理速度が速くなります。
- spawn: プロセス間でコンテキストスイッチが発生するため、forkよりも処理速度が遅くなります。
適用ケース
- fork: 同じNode.jsスクリプトを複数回実行したり、親プロセスと子プロセス間で頻繁にデータ共有を行ったりする場合に適しています。
- spawn: 外部プログラムを実行したり、異なる言語で書かれたプログラムと連携したりする場合に適しています。
- いずれの方法も、非同期処理で実行されます。
- forkは、共有メモリを使用するため、セキュリティ面での注意が必要です。
- forkはNode.jsスクリプトのみを複製できるため、外部プログラムを実行することはできません。
機能 | spawn | fork |
---|---|---|
生成方法 | 別プログラムを実行 | Node.jsプロセスを複製 |
メモリ共有 | なし | あり |
処理速度 | 遅い | 速い |
適用ケース | 外部プログラム実行、異なる言語連携 | 同じスクリプトの複数実行、頻繁なデータ共有 |
その他 | 外部プログラム実行可、セキュリティリスク低い | Node.jsスクリプトのみ、セキュリティリスク高い |
spawnを使用した例
const child_process = require('child_process');
// 子プロセスとして `ls` コマンドを実行
const child = child_process.spawn('ls');
// 子プロセスの標準出力を受信
child.stdout.on('data', (data) => {
console.log(data.toString());
});
// 子プロセスが終了したら終了コードを出力
child.on('exit', (code) => {
console.log(`子プロセスが終了しました。終了コード: ${code}`);
});
forkを使用した例
const child_process = require('child_process');
// 子プロセスとして現在のスクリプトを実行
const child = child_process.fork(__filename);
// 親プロセスから子プロセスへメッセージを送信
child.send('Hello from parent!');
// 子プロセスからのメッセージを受信
child.on('message', (message) => {
console.log(`子プロセスから受信: ${message}`);
});
- シンプルなコマンド実行に適しています。
- 引数としてコマンド文字列を渡し、そのコマンドの出力をPromiseで返します。
child_process.exec
モジュールを使用して、シェルコマンドを実行します。
const child_process = require('child_process');
child_process.exec('ls', (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.log(stdout);
});
execFile:
- spawnと同様に、外部プログラムを実行する場合に適しています。
- プログラムのパスと引数を引数として渡し、そのプログラムの出力をPromiseで返します。
const child_process = require('child_process');
child_process.execFile('ls', ['-l'], (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.log(stdout);
});
worker_threads:
- 計算量が多いタスクや、親プロセスと子プロセス間で頻繁にデータ共有を行う場合に適しています。
- 従来の子プロセスよりも効率的なデータ共有と並行処理が可能ですが、Node.js 16以降でのみ利用可能です。
worker_threads
モジュールを使用して、共有メモリを介してWorkerスレッドと通信する子プロセスを作成します。
const { Worker } = require('worker_threads');
const worker = new Worker('./worker.js');
worker.onmessage = (message) => {
console.log(`Workerから受信: ${message.data}`);
};
worker.postMessage('Hello from main thread!');
- 具体的なニーズに応じて、適切なライブラリを選択することが重要です。
- 上記以外にも、Node.jsには様々な子プロセス関連のモジュールやライブラリが存在します。
node.js process parent-child