ホームページ > 記事 > ウェブフロントエンド > Node.js のワーカー スレッドについての深い理解
[推奨学習: 「nodejs チュートリアル 」]
ワーカーを理解するには、ノードの最下層を理解する必要があります。
Node.js アプリケーションが開始されると、次のモジュールが開始されます:
プロセス: プロセス オブジェクトは、Node.js プログラム内のどこからでもアクセスできるグローバル変数です。そして、現在のプロセスに関する情報を提供します。
1 スレッド: シングル スレッドとは、現在のプロセスで同時に実行される命令が 1 つだけであることを意味します。
イベント ループ: これは理解する必要がある Node.js の一部です。JavaScript はシングルスレッドですが、コールバック、プロミス、非同期を使用したイベントに基づいています。 /await およびその他の構文。ループはオペレーティング システムを非同期的に動作させ、ノードが非同期ノンブロッキング IO の特性を持つことを可能にします。
JS エンジン インスタンス: JavaScript コードを実行できるプログラム。
Node.js インスタンス: Node.js 環境を実行できるプログラム。
言い換えると、Node は単一のスレッドで実行され、イベント ループでは同時に 1 つのプロセス タスクのみが実行され、毎回 1 つのコードのみが同時に実行されます (複数のコード)。のコードは同時に実行されません)。仕組みがシンプルなので、JavaScript を使用する際に同時プログラミングを気にする必要がないため、これは非常に効果的です。
その理由は、JavaScript はもともとクライアント側の対話 (Web ページ対話やフォーム検証など) に使用されており、これらのロジックの処理にはマルチスレッドなどのメカニズムが必要ないためです。
したがって、これには別の欠点も伴います。メモリ内の大規模なデータセットを使用して複雑な計算を実行するなど、CPU を集中的に使用するタスクを使用する必要がある場合、他のプロセスのタスクがブロックされてしまいます。同様に、CPU を集中的に使用するタスクを含むリモート インターフェイス リクエストを開始すると、実行する必要がある他のリクエストもブロックされます。
関数が次の関数が実行可能になるまでイベント ループ メカニズムをブロックする場合、その関数はブロック関数とみなされます。ノンブロッキング関数は、次の関数の実行のためにイベント ループをブロックせず、コールバックを使用してタスクが完了したことをイベント ループに通知します。
ベスト プラクティス: イベント ループをブロックせず、イベント ループを継続的に実行し続けます。また、同期ネットワーク インターフェイス呼び出しや無限ループなど、スレッドをブロックする操作の使用を避けるように注意してください。
CPU を集中的に使用する操作と I/O (入出力) を集中的に使用する操作を区別することが重要です。前述したように、Node.js は非同期であるため、複数のコードを同時に実行することはなく、I/O 操作のみが同時に実行されます。
したがって、非同期 I/O 操作はワーカーよりも効率的であるため、ワーカー スレッドは I/O 集中型の操作にはあまり役に立ちません。ワーカーの主な役割は、CPU 集中型の操作のパフォーマンスを向上させることです。
さらに、フル活用を保証するマルチプロセス (クラスター API) ソリューションなど、CPU を集中的に使用する操作向けのソリューションがすでに多数あります。マルチコアCPUの。
このソリューションの利点は、プロセスが互いに独立しているため、1 つのプロセスで問題が発生しても、他のプロセスに影響を与えないことです。さらに、安定した API も備えていますが、これはメモリ空間を共有できず、プロセス間通信は JSON 形式のデータを介してのみ発生することを意味します。
そこで、スレッドを同期します。これにより、CPU を集中的に使用する操作のニーズを解決できます。
ただし、マルチスレッドモジュールを追加すると、言語自体の特性が変化します。マルチスレッド モジュールを使用可能なクラスまたは関数として追加することはできません。 Java など、マルチスレッドをサポートする一部の言語では、複数のスレッド間の同期を可能にするために同期機能が使用されます。
そして、一部の数値型は十分にアトミックではありません。つまり、数値型を同期的に操作しないと、複数のスレッドが同時に計算を実行すると、変数の値が変化し続け、明確な値がなくなる可能性があります。変数の値は、あるスレッドによる計算後に数バイト変更され、別のスレッドによる計算後に数バイトのデータが変更される場合があります。たとえば、JavaScript では、0.1 0.2 のような単純な計算の結果は、10 進数 17 桁 (10 進数の最大桁数) になります。
var x = 0.1 + 0.2; // x will be 0.30000000000000004
しかし、浮動小数点数の計算は 100% 正確ではありません。したがって、計算が同期されていない場合、複数のスレッドが原因で数値の小数部分は決して正確な数値になりません。
したがって、CPU 集中型の操作のパフォーマンスの問題を解決するには、ワーカー スレッドを使用します。ブラウザには長い間 Workers 機能が搭載されてきました。
単一スレッドの Node.js:
多线程 Workers 下 Node.js 拥有:
就像下图:
Worker_threads 模块允许使用多个线程来同时执行 JavaScript 代码。使用下面这个方式引入:
const worker = require('worker_threads');
Worker Threads 已经被添加到 Node.js 10 版本中,但是仍处于实验阶段。
使用 Worker threads 我们可以在在同一个进程内可以拥有多个 Node.js 实例,并且线程可以不需要跟随父进程的终止的时候才被终止,它可以在任意时刻被终止。当 Worker 线程销毁的时候分配给该 Worker 线程的资源依然没有被释放是一个很不好的操作,这会导致内存泄漏问题,我们也不希望这样。我们希望这些分配资源能够嵌入到 Node.js 中,让 Node.js 有创建线程的能力,并且在线程中创建一个新的 Node.js 实例,本质上就像是在同一个进程中运行多个独立的线程。
Worker Threads 有如下特性:
ArrayBuffers
可以将内存中的变量从一个线程转到另外一个SharedArrayBuffer
可以在多个线程中共享内存中的变量,但是限制为二进制格式的数据。可用的原子操作
,可以让你更有效率地同时执行某些操作并且实现竞态变量消息端口
,用于多个线程间通信。可以用于多个线程间传输结构化的数据,内存空间消息通道
就像多线程间的一个异步的双向通信通道。WorkerData
是用于传输启动数据。在多个线程间使用 postMessgae 进行传输的时候,数据会被克隆,并将克隆的数据传输到线程的 contructor 中。API:
const { worker, parantPort } = require('worker_threads');
=>worker
函数相当于一个独立的 JavaScript 运行环境线程,parentPort 是消息端口的一个实例new Worker(filename)
or new Worker(code, { eval: true })
=>启动 worker 的时候有两种方式,可以通过传输文件路径或者代码,在生产环境中推荐使用文件路径的方式。worker.on('message')
,worker.postMessage(data)
=> 这是多线程间监听事件与推送数据的方式。parentPort.on('message')
, parentPort.postMessage(data)
=> 在线程中使用 parentPort.postMessage
方式推送的数据可以在父进程中使用 worker.on('message')
的方式接收到,在父进程中使用 worker.postMessage()
的方式推送的数据可以在线程中使用 parentPort.on('message')
的方式监听到。const { Worker } = require('worker_threads'); const worker = new Worker(` const { parentPort } = require('worker_threads'); parentPort.once('message', message => parentPort.postMessage({ pong: message })); `, { eval: true }); worker.on('message', message => console.log(message)); worker.postMessage('ping');
$ node --experimental-worker test.js { pong: ‘ping’ }
上面例子所做的也就是使用 new Worker 创建一个线程,线程中的代码监听了 parentPort
的消息,并且当接收到数据的时候只触发一次回调,将收到的数据传输回父进程中。
你需要使用 --experimental-worker
启动程序因为 Workers 还在实验阶段。
另一个例子:
const { Worker, isMainThread, parentPort, workerData } = require('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)); }
上面代码中:
Worker
: 相当于一个独立的 JavaScirpt 运行线程。isMainThread
: 如果为 true 的话说明代码不是运行在 Worker 线程中parentPort
: 消息端口被使用来进行线程间通信workerData
:被传入 worker 的 contructor 的克隆数据。在实际使用中,应该使用线程池的方式,不然不断地创建 worker 线程的代价将会超过它带来的好处。
は実験的なモジュールです。Node.js で CPU を集中的に使用する操作を実行する必要がある場合、実稼働環境でワーカー スレッドを使用することは現在推奨されていません。代わりにプロセス プールを使用できます。
プログラミング ビデオ著者: Liz Parody
その他のプログラミング関連知識については、
以上がNode.js のワーカー スレッドについての深い理解の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。