Heim  >  Artikel  >  Web-Frontend  >  Wie man versteht, dass Node.js kein vollständig Single-Threaded-Programm ist (eine kurze Analyse)

Wie man versteht, dass Node.js kein vollständig Single-Threaded-Programm ist (eine kurze Analyse)

青灯夜游
青灯夜游nach vorne
2022-02-08 18:20:251881Durchsuche

Warum sagen wir, dass Node.js nicht vollständig Single-Threaded ist? Wie ist das zu verstehen? Der folgende Artikel wird mit Ihnen darüber diskutieren, ich hoffe, er wird Ihnen hilfreich sein!

Wie man versteht, dass Node.js kein vollständig Single-Threaded-Programm ist (eine kurze Analyse)

Ich glaube, jeder weiß, dass node ein Single-Thread-Programm ist, das Event Loop verwendet, um mehrere Parallelitäten zu erreichen. Leider ist das nicht ganz richtig.

Warum ist Node.js kein vollständig Single-Threaded-Programm?

Node.js ist ein Single-Thread-Programm*

Alle Javsacript-, V8- und Event-Schleifen, die wir selbst geschrieben haben, laufen im selben Thread, dem Haupt-Thrad.

Hey, bedeutet das nicht, dass der Knoten Single-Threaded ist?

Aber vielleicht wissen Sie nicht, dass Node viele Module mit C++-Code dahinter hat.

Obwohl Node Benutzern nicht die Berechtigung zur Steuerung von Threads erteilt, kann C++ Multithreading verwenden.

Wann wird der Knoten Multithreading verwenden?

  • Wenn eine Knotenmethode hinter den Kulissen die synchronization-Methode von C++ aufruft, wird alles im Hauptthread ausgeführt.

  • Wenn eine Knotenmethode hinter den Kulissen die asynchrone-Methode von C++ aufruft, wird sie manchmal nicht im Hauptthread ausgeführt.

Talk ist günstig, zeig mir den Code

Synchrone Methode, läuft im Hauptthread

Hier crypto Verwandte Module, viele sind in C++ geschrieben. Das folgende Programm ist eine Funktion zur Hash-Berechnung, die im Allgemeinen zum Speichern von Passwörtern verwendet wird.

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`);

Ausgabezeit,

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

Es ​​ist ersichtlich, dass es jedes Mal etwa 45 ms dauert und der Code sequentiell im Hauptthread ausgeführt wird.

Achten Sie darauf, wer die endgültige Ausgabe ist? Beachten Sie, dass ein Hash hier auf meiner CPU ca. 45 ms dauert.

Die asynchrone pbkdf2-Methode wird nicht im Hauptthread ausgeführt

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");

Die Ausgabezeit,

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

Wie Sie hier sehen können, endet der Hauptthread früh, aber jede Berechnungszeit beträgt 45 ms. Sie müssen wissen, dass a Die CPU berechnet den Hash. Die Zeit beträgt 45 ms. Der Knoten verwendet hier definitiv mehrere Threads für die Hash-Berechnung.

Wenn ich hier die Anzahl der Aufrufe auf 10 ändere, dann ist die Zeit wie folgt. Sie können sehen, dass mit zunehmender Anzahl der CPU-Kerne auch die Zeit zunimmt. Wieder einmal wurde bewiesen, dass der Knoten definitiv mehrere Threads für die Hash-Berechnung verwendet.

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

Obwohl hier bewiesen ist, dass auf dem Knoten definitiv Multithreading aktiviert ist. Aber ein kleines Problem? Die CPU meines Computers ist AMD R5-5600U, die über 6 Kerne und 12 Threads verfügt. Aber warum verlängert sich die Zeit ab dem fünften Mal? Node nutzt meine CPU nicht vollständig aus?

Was ist der Grund?

Knoten verwendet einen vordefinierten Thread-Pool.

export UV_THREADPOOL_SIZE=6

Schauen wir uns ein Beispiel an:

HTTP-Anfrage

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

Das Hauptprogramm hier ist Auch hier endete die HTTP-Anfrage zum Herunterladen von Bildern 15 Mal. Die Zeit, die sie brauchten, erhöhte sich nicht exponentiell und schien nicht durch den Thread-Pool/die CPU begrenzt zu sein.

Warum? ? Verwendet Node einen Thread-Pool?

Wenn die C++-Methode asynchronous hinter Node steht, wird zunächst versucht, festzustellen, ob Kernel-Asynchronitätsunterstützung vorhanden ist. Bitte verwenden Sie hier epoll (Linux) für das Netzwerk. Wenn der Kernel keine asynchrone Methode bereitstellt, Node wird seinen eigenen Thread-Pool verwenden. .

Obwohl die HTTP-Anforderung asynchron ist, wird sie vom Kernel implementiert. Wenn der Kernel abgeschlossen ist, wird C++ benachrichtigt und C++ benachrichtigt den Hauptthread, um den Rückruf zu verarbeiten.

Welche asynchronen Methoden in Node verwenden also den Thread-Pool? Welche nicht?

  • Native Kernal Async

    • TCP/UDP-Server-Client
    • Unix Domain Sockets (IPC)
    • pipes
    • dns.resolveXXX
    • tty-Eingabe (stdin usw.)
    • Unix. Signale
    • Untergeordnete Prozess
  • Thread-Pool

    • fs.*
    • dns.lookup
    • pipe (Edge-Fall)

Dies ist auch der Einstiegspunkt für die meisten Knotenoptimierungen.

Aber wie lassen sich diese mit dem wichtigsten Event Loop kombinieren?

Event Loop

Ich glaube, jeder ist mit Event Loop bestens vertraut. Die Ereignisschleife ist wie ein Verteiler.

  • Wenn sie auf ein gewöhnliches Javascript-Programm oder einen Rückruf trifft, wird sie zur Verarbeitung an V8 übergeben.

  • Wenn Sie auf eine in C++ geschriebene synchronisierte Methode stoßen, übergeben Sie sie an C++ und führen Sie sie im Hauptthread aus.

  • Wenn Sie auf eine asynchrone-Methode stoßen, ist die Rückseite in C++ geschrieben. Wenn Kernel-Asynchronitätsunterstützung vorhanden ist, übergeben Sie sie vom Hauptthread an den Kernel zur Verarbeitung.

  • Wenn es asynchron ist, wird die Rückseite der Methode in C++ geschrieben. Wenn keine asynchrone Kernelunterstützung vorhanden ist, wird sie vom Hauptthread an den Thread-Pool übergeben.

  • Der Thread-Pool und der Kernel geben die Ergebnisse an die Ereignisschleife zurück. Wenn ein Javascript-Rückruf registriert ist, wird dieser zur Verarbeitung an V8 übergeben.

Dann geht der Zyklus weiter, bis nichts mehr zu verarbeiten ist.

Node ist also nicht gerade ein Single-Threaded-Programm.

Weitere Informationen zu Knoten finden Sie unter: nodejs-Tutorial!

Das obige ist der detaillierte Inhalt vonWie man versteht, dass Node.js kein vollständig Single-Threaded-Programm ist (eine kurze Analyse). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen