Heim  >  Artikel  >  Web-Frontend  >  Ein tiefgreifendes Verständnis der Worker-Threads in Node.js

Ein tiefgreifendes Verständnis der Worker-Threads in Node.js

青灯夜游
青灯夜游nach vorne
2021-06-28 11:25:402924Durchsuche

Ein tiefgreifendes Verständnis der Worker-Threads in Node.js

【Empfohlene Studie: „nodejs Tutorial“】

Um Workers zu verstehen, ist es notwendig, die unterste Ebene von Node zu verstehen.

Wenn eine Node.js-Anwendung gestartet wird, startet sie die folgenden Module:

  • Ein Prozess
  • Ein Thread
  • Ereignisschleifenmechanismus
  • JS-Engine-Instanz
  • Node.js-Instanz

Ein Prozess: Der Das Prozessobjekt ist eine globale Variable, auf die überall in einem Node.js-Programm zugegriffen werden kann und die Informationen über den aktuellen Prozess bereitstellt.

Ein Thread: Ein einzelner Thread bedeutet, dass im aktuellen Prozess nur eine Anweisung gleichzeitig ausgeführt wird.

Ereignisschleife: Dies ist ein wichtiger Teil von Node.js, der verstanden werden muss. Obwohl JavaScript Single-Threaded ist, werden durch die Verwendung von Syntax wie Callbacks, Promises, Async/Await usw. die Vorgänge des Betriebs verstanden Das System wird basierend auf der Ereignisschleife asynchron sein, wodurch der Knoten die Eigenschaften eines asynchronen, nicht blockierenden E/A aufweist.

Eine JS-Engine-Instanz: ein Programm, das JavaScript-Code ausführen kann.

Eine Node.js-Instanz: ein Programm, das die Node.js-Umgebung ausführen kann.

Mit anderen Worten: Der Knoten wird in einem einzelnen Thread ausgeführt, und in der Ereignisschleife wird jeweils nur eine Prozessaufgabe und jeweils nur ein Codeteil gleichzeitig ausgeführt (mehrere Codeteile werden nicht ausgeführt). gleichzeitig ausgeführt werden). Dies ist sehr effektiv, da der Mechanismus so einfach ist, dass Sie sich bei der Verwendung von JavaScript keine Gedanken über die gleichzeitige Programmierung machen müssen.

Der Grund dafür ist, dass JavaScript ursprünglich für die clientseitige Interaktion (z. B. Webseiteninteraktion oder Formularvalidierung) verwendet wurde und für die Verarbeitung dieser Logiken kein Mechanismus wie Multithreading erforderlich ist.

Das bringt also auch einen weiteren Nachteil mit sich: Wenn Sie CPU-intensive Aufgaben ausführen müssen, z. B. die Durchführung komplexer Berechnungen mit einem großen Datensatz im Speicher, werden die Aufgaben anderer Prozesse blockiert. Wenn Sie eine Remote-Schnittstellenanfrage mit einer CPU-intensiven Aufgabe initiieren, werden auch andere Anfragen blockiert, die ausgeführt werden müssen.

Wenn eine Funktion den Ereignisschleifenmechanismus blockiert, bis die nächste Funktion ausgeführt werden kann, bis diese Funktion ausgeführt wird, dann gilt sie als blockierende Funktion. Eine nicht blockierende Funktion blockiert die Ereignisschleife nicht für die Ausführung der nächsten Funktion. Sie benachrichtigt die Ereignisschleife über einen Rückruf, dass die Aufgabe abgeschlossen ist.

Best Practice: Blockieren Sie nicht die Ereignisschleife, lassen Sie die Ereignisschleife kontinuierlich laufen und achten Sie darauf, Vorgänge zu vermeiden, die den Thread blockieren, wie z. B. synchrone Netzwerkschnittstellenaufrufe oder Endlosschleifen.

Es ist wichtig, zwischen CPU-intensiven Vorgängen und I/O-intensiven Vorgängen (Eingabe/Ausgabe) zu unterscheiden. Wie bereits erwähnt, führt Node.js nicht mehrere Codeteile gleichzeitig aus, da sie asynchron sind.

Daher sind Worker-Threads für E/A-intensive Vorgänge nicht sehr hilfreich, da asynchrone E/A-Vorgänge effizienter sind als Worker. Die Hauptaufgabe von Workern besteht darin, die Leistung von CPU-intensiven Vorgängen zu verbessern.

Andere Lösungen

Darüber hinaus gibt es bereits viele Lösungen für CPU-intensive Vorgänge, wie zum Beispiel die Multi-Process (Cluster API)-Lösung, die die volle Auslastung von Multi-Core-CPUs gewährleistet.

Der Vorteil dieser Lösung besteht darin, dass die Prozesse unabhängig voneinander sind. Wenn in einem Prozess ein Problem auftritt, hat dies keine Auswirkungen auf andere Prozesse. Darüber hinaus verfügen sie auch über eine stabile API. Dies bedeutet jedoch auch, dass der Speicherplatz nicht gemeinsam genutzt werden kann und die Kommunikation zwischen Prozessen nur über Daten im JSON-Format erfolgen kann.

JavaScript und Node.js werden nicht multithreadfähig sein, und hier ist der Grund:

Man könnte also denken, dass das Hinzufügen eines Node.js-Kernmoduls, das Threads erstellt und synchronisiert, den Bedarf an CPU-intensiven Vorgängen beseitigen würde.

Wenn Sie jedoch ein Multithreading-Modul hinzufügen, ändern sich dadurch die Eigenschaften der Sprache selbst. Es ist nicht möglich, das Multithreading-Modul als verfügbare Klasse oder Funktion hinzuzufügen. In einigen Sprachen, die Multithreading unterstützen, wie z. B. Java, werden Synchronisierungsfunktionen verwendet, um die Synchronisierung zwischen mehreren Threads zu ermöglichen.

Und einige numerische Typen sind nicht atomar genug, was bedeutet, dass sich der Wert der Variablen möglicherweise ständig ändert, wenn Sie sie nicht synchron betreiben, wenn mehrere Threads gleichzeitig Berechnungen durchführen Variable kann einige Bytes nach der Berechnung durch einen Thread ändern, und mehrere andere Datenbytes werden nach der Berechnung durch einen anderen Thread geändert. In JavaScript hat beispielsweise das Ergebnis einer einfachen Berechnung wie 0,1 + 0,2 17 Dezimalstellen (die höchste Anzahl an Dezimalstellen).

var x = 0.1 + 0.2; // x will be 0.30000000000000004

Aber die Berechnung von Gleitkommazahlen ist nicht 100 % genau. Wenn die Berechnungen also nicht synchronisiert sind, wird der Dezimalteil der Zahl aufgrund mehrerer Threads nie eine genaue Zahl sein.

Best Practice

Die Lösung des Leistungsproblems CPU-intensiver Vorgänge besteht also in der Verwendung von Worker-Threads. Browser verfügen schon seit langem über Workers-Funktionen.

Node.js unter einem einzelnen Thread:

  • 一个进程
  • 一个线程
  • 一个事件循环
  • 一个 JS 引擎实例
  • 一个 Node.js 实例

多线程 Workers 下 Node.js 拥有:

  • 一个进程
  • 多个线程
  • 每个线程都拥有独立的事件循环
  • 每个线程都拥有一个 JS 引擎实例
  • 每个线程都拥有一个 Node.js 实例

就像下图:

Ein tiefgreifendes Verständnis der Worker-Threads in 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 线程的代价将会超过它带来的好处。

Vorschläge zur Verwendung von Worker:

  • Native Handles wie Sockets, HTTP-Anfragen übertragen
  • Deadlock-Erkennung. Deadlock ist eine Situation, in der mehrere Prozesse blockiert werden, weil jeder Prozess einen Teil einer Ressource enthält und darauf wartet, dass ein anderer Prozess die von ihm gehaltene Ressource freigibt. Die Deadlock-Erkennung ist eine sehr nützliche Funktion in Workers-Threads.
  • Bessere Isolierung, d. h. wenn ein Thread betroffen ist, hat dies keine Auswirkungen auf andere Threads.

Einige schlechte Gedanken über Workers:

  • Glauben Sie nicht, dass Workers unglaubliche Geschwindigkeitsverbesserungen bringen, manchmal ist die Verwendung eines Thread-Pools die bessere Wahl.
  • Verwenden Sie Worker nicht, um E/A-Vorgänge parallel auszuführen.
  • Glauben Sie nicht, dass die Kosten für die Erstellung eines Worker-Prozesses sehr niedrig sind.

Endlich

Chrome devTools unterstützt die Workers-Thread-Funktion in Node.js. worker_threads ist ein experimentelles Modul. Wenn Sie CPU-intensive Vorgänge in Node.js ausführen müssen, wird die Verwendung von Worker-Threads in einer Produktionsumgebung derzeit nicht empfohlen.

Englische Originaladresse: https://nodesource.com/blog/worker-threads-nodejs

Autor: Liz Parody

Weitere Kenntnisse zum Thema Programmierung finden Sie unter: Programmiervideo! !

Das obige ist der detaillierte Inhalt vonEin tiefgreifendes Verständnis der Worker-Threads in Node.js. 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