Node.js がシングルスレッドのイベント駆動型の非同期 I/O モデルを使用していることは誰もが知っていますが、その特性により、マルチコア CPU を活用できず、一部の非 I/O 操作を完了するのが苦手です (スクリプトの実行、AI コンピューティング、画像処理など) を解決するために、Node.js は従来のマルチプロセス (スレッド) ソリューションを提供します (プロセスとスレッドに関する議論については、著者の説明を参照してください)。他の記事 Node.js と同時実行モデル )、この記事では Node.js のマルチスレッド メカニズムを紹介します。
child_process
モジュールを使用して、いくつかの特別なタスク (スクリプトの実行など) を完了するために Node.js の子プロセスを作成できます。このモジュールは主にexec
、 execFile
、 fork
、 spwan
およびその他のメソッドを提供します。 。 使用。
const { exec } = require('child_process'); exec('ls -al', (エラー、標準出力、標準エラー出力) => { console.log(標準出力);このメソッドは
、
options.shell
で指定された実行可能ファイルに従ってコマンド文字列を処理し、コマンドの実行中にその出力をキャッシュし、コマンドの実行が完了するまでコールバック関数のパラメーターの形式で実行結果を返します。
このメソッドのパラメータは次のように説明されます:
command
: 実行されるコマンド ( ls -al
など);
options
: パラメータ設定 (オプション)、関連するプロパティは次のとおりです:
cwd
: 子プロセスの現在の作業ディレクトリ、デフォルトはprocess.cwd()
値です;
shell
env
デフォルト値はprocess.env
の値です;
encoding
: 文字エンコーディング、デフォルト値は次のとおりです: utf8
;
コマンド文字列を処理するファイルUnix
のデフォルト値は/bin/sh
、 Windows
のデフォルト値はprocess.env.ComSpec
の値です (空の場合はcmd.exe
です)。例:
const { exec 。 = require('child_process'); exec("print('Hello World!')", { シェル: 'python' }, (error, stdout, stderr) => { console.log(標準出力); });
上記の例を実行すると、 Hello World!
が出力されます。これはpython -c "print('Hello World!')"
コマンドを実行するサブプロセスと同等です。そのため、この属性を使用する場合は、次の点に注意する必要があります。指定された実行可能ファイルは、 -c
オプションによる関連ステートメントの実行をサポートする必要があります。
注: Node.js
-c
オプションもサポートしていますが、これは--check
オプションと同等であり、指定されたスクリプトに構文エラーがあるかどうかを検出するためにのみ使用され、関連するスクリプトは実行されません。
signal
: 指定された AbortSignal を使用して子プロセスを終了します。この属性は v14.17.0 以降で使用できます。例:
const { exec } = require('child_process'); const ac = new AbortController(); exec('ls -al', { signal: ac.signal }, (error, stdout, stderr) => {});
上記の例では、 ac.abort()
を呼び出すことで子プロセスを早期に終了できます。
timeout
: 子プロセスのタイムアウト時間 (この属性の値が0
より大きい場合、子プロセスの実行時間が指定された値を超えると、属性killSignal
で指定された終了シグナルが子プロセスに送信されます) )、デフォルト値は0
です。
maxBuffer
: stdout または stderr で許可される最大キャッシュ (バイナリ) を超えると、子プロセスが強制終了され、出力は1024 * 1024
切り捨てられます
killSignal
: 子プロセスの終了シグナル、デフォルト値はSIGTERM
;
uid
: 子プロセスを実行するためのuid
;
gid
: 子プロセスを実行するためのgid
;
windowsHide
: Windows
システムで一般的に使用される、子プロセスのコンソール ウィンドウを非表示にするかどうかデフォルト値はfalse
です;
callback
: error
、 stdout
、 stderr
を含むコールバック関数 パラメータ:
error
: コマンドラインが正常に実行された場合、値はnull
、それ以外の場合、値は Error のインスタンスであり、 error.code
が終了します。子プロセスのエラー コード、 error.signal
は子プロセスの終了のシグナルです。stdout
およびstderr
: child encoding
encoding
がbuffer
の場合、プロセスのstdout
およびstderr
エンコードされます。 、またはstdout
またはstderr
の値が認識できない文字列である場合は、 buffer
に従ってエンコードされます。const { execFile } = require('child_process'); execFile('ls', ['-al'], (error, stdout, stderr) => { console.log(標準出力);このメソッドの機能は exec と似ていますが
、
exec
の違いは、 execFile
デフォルトで指定された実行可能ファイル (つまり、パラメータfile
の値) を使用してコマンドを直接処理するため、効率がexec
よりわずかに高くなる点です。 (シェルの処理ロジックを見ると、効率は無視できるほど低いと感じます)。
このメソッドのパラメーターは次のように説明されます:
file
: 実行可能ファイルの名前またはパス;
args
: 実行可能ファイルのパラメーターのリスト;
shell
options
。
false
指定された実行可能ファイル (つまり、パラメータfile
の値) を直接使用してコマンドを処理します。値がtrue
またはその他の文字列の場合、関数はexec
のshell
と同等です。デフォルト値はfalse
です。maxBuffer
gid
cwd
uid
encoding
timeout
killSignal
env
windowsVerbatimArguments
Windows
するか。Unix Unix
、デフォルト値はfalse
です。windowsHide
、およびsignal
は上で紹介したので、ここでは繰り返しません。callback
: コールバック関数exec
のcallback
と同等なので、ここでは説明しません。
const { fork } = require('child_process'); const echo = fork('./echo.js', { サイレント: 本当 }); echo.stdout.on('データ', (データ) => { console.log(`stdout: ${data}`); }); echo.stderr.on('データ', (データ) => { console.error(`stderr: ${data}`); }); echo.on('close', (コード) => { console.log(`子プロセスはコード ${code}` で終了しました);このメソッドは
、
指定された Node.js スクリプトを実行し、IPC 経由で親プロセスと通信するための新しい Node.js インスタンスを作成するために使用されます。
このメソッドのパラメータは次のように説明されます:
modulePath
: 実行する Node.js スクリプトのパス;
args
: Node.js スクリプトに渡されるパラメータのリスト;
options
: パラメータの設定 (指定不可)、関連する属性例:
detached
: spwan
のoptions.detached
を参照;
execPath
: 子プロセスの実行可能ファイルを作成します;
execArgv
: 実行可能ファイルに渡される文字列パラメータのリスト、デフォルト値はprocess.execArgv
の値です
serialization
:インタープロセスのメッセージのシリアル番号タイプ利用可能な値はjson
でありadvanced
json
stderr
stdout
stdin
slient
true
false
を介して、親のプロセスのstdin
、 stdout
、 stderr
継承さ
stdio
options.stdio
spwan
ここで注意する必要があるのは
slient
の値を無視するipc
[0, 1, 2, 'ipc']
あることProperties cwd
、 env
、 uid
、 gid
、 windowsVerbatimArguments
、 signal
、 timeout
、 killSignal
が上に導入されており、ここでは繰り返されません。
const { spawn } = require('child_process'); const ls = spawn('ls', ['-al']); ls.stdout.on('データ', (データ) => { console.log(`stdout: ${data}`); }); ls.stderr.on('データ', (データ) => { console.error(`stderr: ${data}`); }); ls.on('close', (コード) => { console.log( `code $ {code}`)で終了した子プロセスこのメソッドは
、
child_process
モジュールの基本メソッドです。 exec
、 execFile
、およびfork
、最終的にspawn
呼び出して子プロセスを作成します。
このメソッドのパラメータは次のように説明されます:
command
: 実行可能ファイルの名前またはパス;
args
: 実行可能ファイルに渡されるパラメータのリスト;
options
: パラメータの設定 (指定不可)、関連する属性は次のとおりです。
detached
argv0
command
値です。
プロセスは実行され続けることができます)、デフォルト値はfalse
であり、その値がtrue
である場合、各プラットフォームは効果が次のとおりです。Windows
Windows
では、親プロセスが終了した後、子プロセスが実行され続けることがあり、子プロセスWindows
でunref
早期に終了することを望んでいる場合、次のポイントを同時に満たす必要があることに注意してください。
detached
。 true
に設定します。stdiostdio
ignore
。たとえば、次の例:
// hello.js const fs = require('fs'); インデックス = 0 とします。 関数 run() { setTimeout(() => { fs.writeFileSync('./hello', `index: ${index}`); if (インデックス < 10) { インデックス += 1; 走る(); } }, 1000); } 走る(); // メイン.js const { spawn } = require('child_process'); const child = spawn('node', ['./hello.js'], { 切り離された: true、 標準オーディオ: '無視' }); child.unref();
stdio
: 子プロセスの標準入出力構成、デフォルト値はpipe
、値は文字列または配列です。
pipe
['pipe', 'pipe', 'pipe']
に変換されます)、値が配列の場合、使用可能な値は、 pipe
、 overlapped
、 ignore
、 inherit
です。stdin
、 stdout
、 stderr
の構成。項目の使用可能な値は、 pipe
、 overlapped
、 ignore
、 inherit
、 ipc
、Stream オブジェクト、正の整数 (親プロセスで開かれたファイル記述子)、 null
(親プロセスで開かれたファイル記述子である場合) です。配列の最初の 3 つの項目にある場合は、 pipe
と等価です。それ以外の場合は、 ignore
と等価です。)、 undefined
(配列の最初の 3 つの項目にある場合は、 pipe
と等価です。それ以外の場合は、と同等です。 ignore
)。属性cwd
、 env
、 uid
、 gid
、 serialization
、 shell
(値はboolean
またはstring
)、 windowsVerbatimArguments
、 windowsHide
、 signal
、 timeout
、 killSignal
は上で紹介したので、ここでは繰り返しません。
上記は、 child_process
モジュールの主なメソッドの使用法を簡単に説明したものです。 execSync
、 execFileSync
、 forkSync
、およびspwanSync
メソッドは、 exec
、 execFile
、およびspwan
の同期バージョンであるため、パラメータに違いはありません。それらは繰り返されません。
cluster
介して、node.jsプロセスクラスターを作成できます。プログラムの効率; 以下では、 cluster
モジュールの使用方法を紹介します。
const http = require('http'); const クラスター = require('cluster'); const numCPUs = require('os').cpus().length; if (cluster.isPrimary) { for (let i = 0; i < numCPUs; i++) { クラスター.フォーク(); } } それ以外 { http.createServer((req, res) => { res.writeHead(200); res.end(`${process.pid}n`); }).listen(8000);上記の例は
、
cluster.isPrimary
属性の判断(つまり、現在のプロセスがメインプロセスかどうかの判断)に基づいて2つの部分に分かれており、
cluster.fork
呼び出しによる。8000
) で待機します。上記の例を実行し、ブラウザでhttp://localhost:8000/
アクセスすると、返されるpid
アクセスごとに異なることがわかります。これは、リクエストが実際に各子プロセスに分散されていることを示しています。
node_cluster_sched_policy
NODE_CLUSTER_SCHED_POLICY
cluster.schedulingPolicy
プロパティを介して変更できるラウンドロビンスケジューリングです。
もう 1 つの注意点は、各子プロセスが HTTP サーバーを作成し、同じポートをリッスンしているにもかかわらず、これらの子プロセスが自由に競合できるわけではないということです
。
これは、すべての子プロセスの負荷が分散されることを保証できないためです。したがって、メイン プロセスがポートをリッスンし、分散ポリシーに従って処理するためにユーザー要求を特定のサブプロセスに転送するのが正しいプロセスである必要があります。
プロセスは相互に分離されているため、プロセスは通常、共有メモリ、メッセージ パッシング、パイプなどのメカニズムを通じて通信します。
Node.jsは
、次の例のように、消息传递
を通じて親プロセスと子プロセス間の通信を完了します。
const cluster = require( 'cluster'); const numcpus = require( 'os')。cpus()。length; if (cluster.isPrimary) { for(i = 0; i <numcpus; i ++){ const worker = cluster.fork(); worker.on( 'message'、(message)=> { console.log(`私はプライマリ(${process.pid})です。ワーカーからメッセージを受け取りました: "${message}"`); worker.send( `労働者にメッセージを送信) }); } } それ以外 { process.on( 'message'、(message)=> { console.log(`私はワーカー(${process.pid})、プライマリからメッセージを受け取りました: "${message}"`) }); http.createserver((req、res)=> { res.writeHead(200); res.End( `$ {process.pid} n`); process.send( 'プライマリにメッセージを送信'); }).listen(8000); }
上記の例を実行してhttp://localhost:8000/
にアクセスし、端末を確認します。出力が次のように表示されます。
私はプライマリ(44460)、ワーカーからメッセージを受け取りました。「プライマリにメッセージを送信」 私はワーカー(44461)です。プライマリから「ワーカーにメッセージを送信してください」というメッセージを受け取りました。 私はプライマリ (44460) です。ワーカーからメッセージを受け取りました: 「メッセージをプライマリに送信してください」 私はワーカー(44462)です。プライマリからメッセージを受け取りました: 「ワーカーにメッセージを送信」
このメカニズムを使用すると、各子プロセスのステータスを監視できるため、子プロセスで事故が発生したときに、適切なタイミングで介入できます。サービスの可用性を確保するため。
cluster
モジュールのインターフェイスは非常にシンプルです。スペースを節約するために、ここでは、 cluster.setupPrimary
メソッドに関するいくつかの特別な説明のみを記載します。他のメソッドについては、公式ドキュメントを確認してください。cluster.setupPrimary
cluster.setupPrimary
呼び出された後、関連する設定が行われます。は、 cluster.settings
属性に同期され、各呼び出しは現在のcluster.settings
属性の値に基づきます。cluster.setupPrimarycluster.setupPrimary
呼び出された後は、実行中の子プロセスには影響せず、後続のcluster.fork
呼び出しにのみ影響します。影響を受けます。cluster.setupPrimarycluster.setupPrimary
呼び出された後は、 cluster.fork
への以降のパスには影響しません。cluster.setupPrimary 呼び出しのenv
パラメーターはcluster.setupPrimary
プロセスでのみ使用できます。先ほどcluster
モジュールを紹介しました。これにより、Node.js プロセス クラスターを作成してプログラムの実行効率を向上させることができます。ただし、 cluster
マルチプロセス モデルに基づいており、プロセス間の切り替えと分離にコストがかかります。子プロセスの数が増加すると、システム リソースの制約により応答できなくなるという問題が発生しやすくなります。このような問題を解決するために、Node.js はworker_threads
提供します。以下では、具体的な例を通してこのモジュールの使用法を簡単に紹介します
。 const http = require('http'); const { ワーカー } = require('worker_threads'); http.createServer((req, res) => { const httpWorker = new Worker('./http_worker.js'); httpWorker.on('メッセージ', (結果) => { res.writeHead(200); res.end(`${結果}n`); }); httpWorker.postMessage('トム'); }).listen(8000); // http_worker.js const {parentPort } = require('worker_threads'); parentPort.on('メッセージ', (名前) => { ParentPort.PostMessage( `welcone $ {name}!`); })上記の
字符串
JavaScript
worker_threads.Worker
worker_threads
worker_threads
使用する場合、次の点に注意する必要があります。
たとえば、上記の例は次のように変更できます:
const code = "const {parentPort } = require('worker_threads');parentPort.on('message', (name) => {parentPort.postMessage(`Welcone ${名前}!` );})";
httpworker
worker_threads.Worker
workerData
Worker(code、{eval:true});
.js const {worker} = require( 'worker_threads'); const httpWorker = new Worker('./http_worker.js', {workerData: { name: 'Tom'} }); // http_worker.js const {workerData} = require('worker_threads');
const { Worker,
SHARE_ENV
worker_threads.Worker
=
require( 'worker_threads'); const worker = new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV }); worker.on( 'exit'、()=> { console.log(process.env.set_in_worker);
cluster
のプロセス間通信メカニズムとは異なり、 worker_threads
スレッド間の通信に MessageChannel を使用します。
parentPort.postMessage
メソッドを通じてメイン スレッドにメッセージを送信し、 message
をリッスンすることでメイン スレッドからのメッセージを処理します。
parentPort
メッセージのmessage
イベント。postMessage
メソッドを介してhttpWorker
httpWorker
メッセージを送信し、以下のこのワーカーサブスレッドに置き換えられます)。 httpWorker
のmessage
イベントをリッスンすることによって。Node.js では、 cluster
によって作成された子プロセスであっても、 worker_threads
によって作成されたワーカー子スレッドであっても、それらはすべて独自の V8 インスタンスとイベント ループを持っています。違いは、
Worker サブスレッドは子プロセスより効率的であるように見えますが、Worker サブスレッドにも欠点があります。つまり、 cluster
負荷分散を提供しますが、 worker_threads
は負荷分散の設計と実装を自分で完了する必要があるということです。
この記事では、これら3つのモジュールを介して3つのchild_process
、 cluster
、およびworker_threads
の使用を紹介します。スレッド)モード(AI、画像処理など)の動作効率。各モジュールには、この記事では、自分の問題に基づいて効率的に使用する必要があります。最後に、この記事に間違いがあるなら、私はあなたが彼らを毎日幸せにすることを願っています。