Node.js は、ノンブロッキングのイベント駆動型アーキテクチャで知られており、特に I/O バウンドのタスクにおいて、高い同時実行性の処理に優れています。 ただし、CPU を集中的に使用する操作には課題があります。それは、メイン イベント ループのブロックやパフォーマンスへの影響をどのように防ぐかということです。 解決策はワーカースレッドにあります。
この記事では、Node.js ワーカー スレッドについて詳しく説明し、その機能を説明し、C や Java などの言語のスレッドと対比し、計算要求の高いタスクの処理におけるその使用法を示します。
Node.js は本質的にシングルスレッド環境内で動作します。 JavaScript コードは単一のスレッド (イベント ループ) で実行されます。これは非同期 I/O には効率的ですが、大規模なデータセットの処理、複雑な計算、集中的な画像/ビデオ操作など、CPU に依存するタスクではボトルネックになります。
worker_threads
モジュールは、複数のスレッドでの JavaScript コードの並列実行を可能にすることで、この制限に対処します。これらのスレッドは負荷の高い計算をオフロードし、メイン イベント ループの応答性を維持し、アプリケーション全体のパフォーマンスを向上させます。
Node.js ワーカー スレッドはネイティブ OS スレッドであり、従来のマルチスレッド アプリケーションのスレッドと同様にオペレーティング システムによって管理されます。 重要なのは、これらは Node.js のシングルスレッド JavaScript モデル内で動作し、メモリ分離を維持し、メッセージ パッシングを介して通信します。
次の例を考えてみましょう:
<code class="language-javascript">const { Worker, isMainThread, parentPort } = require('worker_threads'); if (isMainThread) { // Main thread: Creates a worker const worker = new Worker(__filename); worker.on('message', (message) => { console.log('Message from worker:', message); }); worker.postMessage('Start processing'); } else { // Worker thread: Handles the task parentPort.on('message', (message) => { console.log('Received in worker:', message); const result = heavyComputation(40); parentPort.postMessage(result); }); } function heavyComputation(n) { // Simulates heavy computation (recursive Fibonacci) if (n <= 1) return n; return heavyComputation(n - 1) + heavyComputation(n - 2); }</code>
ここでは、メインスレッドが同じスクリプトを使用してワーカーを生成します。ワーカーは計算負荷の高いタスク (フィボナッチ数の計算) を実行し、postMessage()
.
ワーカー スレッドの主な機能:
ワーカー スレッドの最適な使用例
次の場合に Node.js でワーカー スレッドを使用します。
大規模なデータセットの処理 (大規模な CSV ファイルの解析、機械学習モデルの実行) では、ワーカー スレッドへのオフロードから大きなメリットが得られます。
CPU 負荷の高いタスクをシミュレートする方法を検討し、ワーカー スレッドの使用による効率の向上を観察してみましょう。
単純な再帰フィボナッチ アルゴリズム (指数関数的複雑さ) を利用して、重い計算をシミュレートします。 (前の例の heavyComputation
関数はこれを示しています。)
大規模なデータセットの並べ替えも、CPU を大量に使用する古典的なタスクです。 乱数の大きな配列を並べ替えることで、これをシミュレートできます。
<code class="language-javascript">const { Worker, isMainThread, parentPort } = require('worker_threads'); if (isMainThread) { // Main thread: Creates a worker const worker = new Worker(__filename); worker.on('message', (message) => { console.log('Message from worker:', message); }); worker.postMessage('Start processing'); } else { // Worker thread: Handles the task parentPort.on('message', (message) => { console.log('Received in worker:', message); const result = heavyComputation(40); parentPort.postMessage(result); }); } function heavyComputation(n) { // Simulates heavy computation (recursive Fibonacci) if (n <= 1) return n; return heavyComputation(n - 1) + heavyComputation(n - 2); }</code>
100 万もの数字を並べ替えるのは時間がかかります。メインスレッドが応答している間に、ワーカースレッドはこれを処理できます。
広い範囲内で素数を生成することも、計算量の多いタスクです。単純な (非効率的な) アプローチは次のとおりです。
<code class="language-javascript">function heavyComputation() { const arr = Array.from({ length: 1000000 }, () => Math.random()); arr.sort((a, b) => a - b); return arr[0]; // Return the smallest element for demonstration }</code>
これには各数値をチェックする必要があり、ワーカー スレッドへのオフロードに適しています。
ワーカー スレッドと他の言語のスレッド
Node.js ワーカー スレッドは C または Java のスレッドとどのように比較されますか?
Node.js Worker Threads | C /Java Threads |
---|---|
No shared memory; communication uses message passing. | Threads typically share memory, simplifying data sharing but increasing the risk of race conditions. |
Each worker has its own independent event loop. | Threads run concurrently, each with its own execution flow, sharing a common memory space. |
Communication is via message passing (`postMessage()` and event listeners). | Communication is via shared memory, variables, or synchronization methods (mutexes, semaphores). |
More restrictive but safer for concurrency due to isolation and message passing. | Easier for shared memory access but more prone to deadlocks or race conditions. |
Ideal for offloading CPU-intensive tasks non-blockingly. | Best for tasks requiring frequent shared memory interaction and parallel execution in memory-intensive applications. |
C と Java では、通常、スレッドはメモリを共有し、変数に直接アクセスできます。これは効率的ですが、複数のスレッドが同じデータを同時に変更する場合、競合状態のリスクが生じます。多くの場合、同期 (ミューテックス、セマフォ) が必要となり、コードが複雑になります。
Node.js ワーカー スレッドはメッセージ パッシングを使用することでこれを回避し、同時アプリケーションの安全性を高めます。 このアプローチはより制限的ではありますが、一般的なマルチスレッド プログラミングの問題を軽減します。
結論
Node.js ワーカー スレッドは、メイン イベント ループをブロックすることなく、CPU 集中型のタスクを処理するための堅牢なメカニズムを提供します。 これらにより並列実行が可能になり、計算量の多い操作の効率が向上します。
C や Java のスレッドと比較して、Node.js ワーカー スレッドは、メモリ分離とメッセージ パッシング通信を強制することにより、よりシンプルで安全なモデルを提供します。これにより、タスクのオフロードがパフォーマンスと応答性に重要なアプリケーションで使用しやすくなります。 Web サーバーの構築、データ分析の実行、大規模なデータセットの処理のいずれの場合でも、ワーカー スレッドによってパフォーマンスが大幅に向上します。
以上がNode.js のワーカー スレッドを理解する: 詳細の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。