ホームページ  >  記事  >  ウェブフロントエンド  >  Node.js が完全なシングルスレッド プログラムではないことを理解する方法 (簡単な分析)

Node.js が完全なシングルスレッド プログラムではないことを理解する方法 (簡単な分析)

青灯夜游
青灯夜游転載
2022-02-08 18:20:251801ブラウズ

なぜ Node.js は完全なシングルスレッドではないと言えるのでしょうか?どのように理解すればよいでしょうか?次の記事でそれについて説明しますので、お役に立てれば幸いです。

Node.js が完全なシングルスレッド プログラムではないことを理解する方法 (簡単な分析)

node はイベント ループを使用して複数の同時実行を実現するシングルスレッド プログラムであることは誰もが知っていると思います。残念ながら、これは完全に正しいわけではありません。

では、なぜ Node.js は完全なシングルスレッド プログラムではないのでしょうか?

Node.js はシングルスレッド プログラムです*

自分たちで作成したすべての Javascript、V8、およびイベント ループは、メインとなる同じスレッドで実行されます。スレッド。

おい、これはノードがシングルスレッドであることを意味するのではないか?

しかし、おそらく、そのノードには C コードを含む多くのモジュールがあることをご存じないかもしれません。

ノードはスレッドを制御する権限をユーザーに公開しませんが、C はマルチスレッドを使用できます。

それでは、ノードはいつマルチスレッドを使用するのでしょうか?

  • ノード メソッドが C の synchronous メソッドをバックグラウンドで呼び出す場合、そのメソッドはすべてメイン スレッドで実行されます。

  • ノード メソッドが C の asynchronous メソッドをバックグラウンドで呼び出すと、メイン スレッドで実行されないことがあります。

話は簡単です。コードを見せてください。

メイン スレッドで実行される同期メソッド

Herecrypto 関連モジュールの多くは C で書かれています。以下のプログラムは、パスワードを保存するために一般的に使用されるハッシュを計算する関数です。

import { pbkdf2Sync } from "crypto";
const startTime = Date.now();
let index = 0;
for (index = 0; index < 3; index++) {
    pbkdf2Sync("secret", "salt", 100000, 64, "sha512");
    const endTime = Date.now();
    console.log(`${index} time, ${endTime - startTime}`);
}
const endTime = Date.now();
console.log(`in the end`);

出力時間、

0 time, 44 
1 time, 90
2 time, 134
in the end

毎回約 45 ミリ秒かかり、コードがメイン スレッドで順番に実行されることがわかります。

最終出力が誰になるかに注目してください。 私の CPU では、ここでのハッシュに最大 45 ミリ秒かかることに注意してください。

非同期 pbkdf2 メソッドはメイン スレッドでは実行されません

import { cpus } from "os";
import { pbkdf2 } from "crypto";
console.log(cpus().length);
let startTime = console.time("time-main-end");
for (let index = 0; index < 4; index++) {
    startTime = console.time(`time-${index}`);
    pbkdf2("secret", `salt${index}`, 100000, 64, "sha512", (err, derivedKey) => {
        if (err) throw err;
        console.timeEnd(`time-${index}`);
    });
}
console.timeEnd("time-main-end");

出力時間

time-main-end: 0.31ms
time-2: 45.646ms
time-0: 46.055ms
time-3: 46.846ms
time-1: 47.159ms

ここでわかるように、メインスレッドが早い ただし、最終的に各計算にかかる時間は 45ms です。CPU がハッシュを計算するのにかかる時間は 45ms であることを知っておく必要があります。ここのノードは間違いなくハッシュ計算に複数のスレッドを使用します。

ここで呼び出し回数を10回に変更すると時間は以下のようになり、CPUコア数を使い切るにつれて時間も増加していることがわかります。もう一度、ノードがハッシュ計算に複数のスレッドを確実に使用していることが証明されました。

time-main-end: 0.451ms
time-1: 44.977ms
time-2: 46.069ms
time-3: 50.033ms
time-0: 51.381ms
time-5: 96.429ms // 注意这里,从第五次时间开始增加了
time-7: 101.61ms
time-4: 113.535ms
time-6: 121.429ms
time-9: 151.035ms
time-8: 152.585ms

ここでは、ノードでマルチスレッドが確実に有効になっていることが証明されています。しかし、少し問題がありますか?私のコンピューターの CPU は AMD R5-5600U で、6 コアと 12 スレッドがあります。しかし、5 回目から時間が増加するのはなぜですか? ノードが CPU を十分に活用していないのでしょうか? ######理由は何ですか?

ノードは事前定義されたスレッド プールを使用します。このスレッド プールのデフォルト サイズは 4 です。

export UV_THREADPOOL_SIZE=6

見てみましょう例:

HTTP リクエスト

import { request } from "https";
const options = {
  hostname: "www.baidu.com",
  port: 443,
  path: "/img/PC_7ac6a6d319ba4ae29b38e5e4280e9122.png",
  method: "GET",
};

let startTime = console.time(`main`);

for (let index = 0; index < 15; index++) {
  startTime = console.time(`time-${index}`);
  const req = request(options, (res) => {
    console.log(`statusCode: ${res.statusCode}`);
    console.timeEnd(`time-${index}`);
    res.on("data", (d) => {
      // process.stdout.write(d);
    });
  });

  req.on("error", (error) => {
    console.error(error);
  });

  req.end();
}

console.timeEnd("main");
main: 13.927ms
time-2: 83.247ms
time-4: 89.641ms
time-3: 91.497ms
time-12: 91.661ms
time-5: 94.677ms
.....
time-8: 134.026ms
time-1: 143.906ms
time-13: 140.914ms
time-10: 144.088ms
ここのメイン プログラムも早期に終了しました。ここでは、写真をダウンロードするための http リクエストを 15 回開始しましたが、費やした時間は 15 回ではありませんでした。乗算すると、スレッドプール/CPUの影響を受けないようです。 ######なぜ? ?ノードはスレッド プールを使用していますか?

ノードの背後にある C の

asynchronous

メソッドが最初にカーネル非同期サポートがあるかどうかを確認しようとする場合、たとえば、ここではネットワークに epoll (Linux) を使用してください。非同期メソッドを提供しない場合、ノードは独自のスレッド プールを使用します。 。

したがって、http リクエストは非同期ですが、カーネルによって実装されており、カーネルが完了すると C に通知され、C はコールバックを処理するようにメインスレッドに通知します。 それでは、Node のどの非同期メソッドがスレッド プールを使用するのでしょうか?どれがそうではないでしょうか?

ネイティブ カーナル非同期

  • TCP/UDP サーバー クライアント

    Unix ドメイン ソケット (IPC)
    • パイプ
    • dns.resolveXXX
    • tty input(stdin など)
    • Unix シグナル
    • 子プロセス
    • スレッド プール
  • fs.*

    dns.lookup
    • pipe (エッジ ケース)
    • #This これは、ほとんどのノード最適化のエントリ ポイントでもあります。
  • しかし、これらを最も重要なイベント ループとどのように組み合わせるのでしょうか?

イベント ループ

イベント ループについては、誰もがよく知っていると思います。イベント ループはディストリビュータのようなもので、

#通常の JavaScript プログラムまたはコールバックが発生すると、処理のために V8 に渡されます。

  • C で記述された同期メソッドが見つかった場合は、それを C に渡し、メインスレッドで実行します。

  • 非同期

    が発生した場合、メソッドの裏側は C で書かれています。カーネル非同期サポートがある場合は、メインスレッドからメソッドに渡されます。処理用のカーネル。
  • 非同期の場合

    メソッドの裏側は C で書かれています。カーネル非同期サポートがない場合は、メインスレッドからメソッドに渡されます。スレッドプール。
  • スレッド プールとカーネルは結果をイベント ループに返します。JavaScript コールバックが登録されている場合は、処理のために V8 に渡されます。

そして、処理するものがなくなるまでこのようにループします。

つまり、Node は完全にシングルスレッド プログラムではありません。

ノード関連の知識の詳細については、nodejs チュートリアル を参照してください。

以上がNode.js が完全なシングルスレッド プログラムではないことを理解する方法 (簡単な分析)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。