Maison  >  Article  >  interface Web  >  Une compréhension approfondie des Worker Threads dans Node.js

Une compréhension approfondie des Worker Threads dans Node.js

青灯夜游
青灯夜游avant
2021-06-28 11:25:402981parcourir

Une compréhension approfondie des Worker Threads dans Node.js

[Étude recommandée : "Tutoriel nodejs"]

Comprendre la couche inférieure de Node est nécessaire pour comprendre Workers.

Lorsqu'une application Node.js est démarrée, elle démarrera les modules suivants :

  • Un processus
  • Un thread
  • Mécanisme de boucle d'événement
  • Instance du moteur JS
  • Instance Node.js

Un processus : L'objet processus est une variable globale accessible n'importe où dans le programme Node.js. Et fournissez des informations sur le processus en cours.

Un seul thread : un seul thread signifie qu'une seule instruction s'exécute en même temps dans le processus en cours.

Boucle d'événements : il s'agit d'une partie de Node.js qui doit être comprise. Bien que JavaScript soit monothread, il est basé sur des événements via l'utilisation de rappels, de promesses et d'async. /await et d'autres syntaxes. La boucle exploite le système d'exploitation de manière asynchrone, permettant à Node d'avoir les caractéristiques d'E/S asynchrones non bloquantes.

Une instance de moteur JS : un programme qui peut exécuter du code JavaScript.

Une instance Node.js : un programme qui peut exécuter l'environnement Node.js.

En d'autres termes, Node s'exécute sur un seul thread, et dans la boucle d'événements, une seule tâche de processus est exécutée en même temps, et un seul morceau de code est exécuté en même temps à chaque fois (plusieurs morceaux du code ne sera pas exécuté en même temps). Ceci est très efficace car le mécanisme est suffisamment simple pour que vous n'ayez pas à vous soucier de la programmation simultanée lors de l'utilisation de JavaScript.

La raison en est que JavaScript était à l'origine utilisé pour l'interaction côté client (telle que l'interaction avec une page Web ou la validation de formulaire), et ces logiques ne nécessitent pas de mécanisme tel que le multithread pour être traitées.

Cela présente donc également un autre inconvénient : si vous devez utiliser des tâches gourmandes en CPU, comme effectuer des calculs complexes en utilisant un grand ensemble de données en mémoire, cela bloquera les tâches d'autres processus. De même, lorsque vous lancez une requête d’interface distante comportant une tâche gourmande en CPU, elle bloquera également les autres requêtes qui doivent être exécutées.

Si une fonction bloque le mécanisme de boucle d'événements jusqu'à ce que la fonction suivante puisse être exécutée, elle est considérée comme une fonction bloquante. Une fonction non bloquante ne bloquera pas la boucle d'événements pour l'exécution de la fonction suivante. Elle utilisera un rappel pour notifier la boucle d'événements que la tâche est terminée.

Bonne pratique : ne bloquez pas la boucle d'événements, maintenez la boucle d'événements en cours d'exécution en continu et veillez à éviter d'utiliser des opérations qui bloquent le thread, telles que des appels d'interface réseau synchrones ou des boucles infinies.

Il est important de faire la distinction entre les opérations gourmandes en CPU et les opérations gourmandes en E/S (entrées/sorties). Comme mentionné précédemment, Node.js n'exécutera pas plusieurs morceaux de code en même temps. Seules les opérations d'E/S seront exécutées en même temps car elles sont asynchrones.

Les Worker Threads ne sont donc pas très utiles pour les opérations gourmandes en E/S, car les opérations d'E/S asynchrones sont plus efficaces que les Workers. Le rôle principal des Wokers est d'améliorer les performances gourmandes en CPU.

Autres solutions

De plus, il existe déjà de nombreuses solutions pour les opérations gourmandes en CPU, comme la solution multi-processus (cluster API), qui garantit une utilisation complète de processeurs multicœurs.

L'avantage de cette solution est que les processus sont indépendants les uns des autres. Si un problème survient dans un processus, il n'affectera pas les autres processus. De plus, ils disposent également d'une API stable, cependant, cela signifie également que l'espace mémoire ne peut pas être partagé et que la communication inter-processus ne peut se produire que via des données au format JSON.

JavaScript et Node.js ne seront pas multi-thread pour les raisons suivantes :

On pourrait donc penser à ajouter un module central Node.js qui crée et synchronise les threads Cela peut résoudre les besoins des opérations gourmandes en CPU.

Cependant, si vous ajoutez un module multi-thread, cela modifiera les caractéristiques du langage lui-même. Il n'est pas possible d'ajouter le module multithreading en tant que classe ou fonction disponible. Dans certains langages prenant en charge le multi-threading, tels que Java, les fonctionnalités de synchronisation sont utilisées pour activer la synchronisation entre plusieurs threads.

Et certains types numériques ne sont pas assez atomiques, ce qui signifie que si vous ne les utilisez pas de manière synchrone, lorsque plusieurs threads effectuent des calculs en même temps, la valeur de la variable peut continuer à changer sans valeur définie. la valeur d'une variable peut changer de quelques octets après le calcul par un thread, et changer plusieurs octets de données après le calcul par un autre thread. Par exemple, en JavaScript, le résultat d'un calcul simple comme 0,1 + 0,2 comporte 17 chiffres décimaux (le nombre le plus élevé de chiffres décimaux).

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

Mais les calculs en virgule flottante ne sont pas précis à 100 %. Ainsi, si les calculs ne sont pas synchronisés, la partie décimale du nombre ne sera jamais un nombre précis en raison de plusieurs threads.

Meilleures pratiques

Résoudre le problème de performances des opérations gourmandes en CPU consiste donc à utiliser des Worker Threads. Les navigateurs disposent de fonctionnalités Workers depuis longtemps.

Node.js sous un seul thread :

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

多线程 Workers 下 Node.js 拥有:

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

就像下图:

Une compréhension approfondie des Worker Threads dans 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 线程的代价将会超过它带来的好处。

Recommandations pour l'utilisation de Worker :

  • Transmission de handles natifs tels que les sockets, les requêtes http
  • Détection de blocage. Un blocage est une situation dans laquelle plusieurs processus sont bloqués car chaque processus détient une partie d'une ressource et attend qu'un autre processus libère la ressource qu'il détient. La détection des blocages est une fonctionnalité très utile dans Workers Threads
  • Meilleure isolation, donc si un thread est affecté, il n'affecte pas les autres threads.

Quelques mauvaises pensées à propos des Workers :

  • Ne pensez pas que les Workers apporteront des améliorations de vitesse incroyables. Parfois, l'utilisation de pools de threads sera meilleure. choix.
  • N'utilisez pas Workers pour effectuer des opérations d'E/S en parallèle.
  • Ne pensez pas que les frais généraux liés à la création d'un processus Worker sont très faibles.

Enfin

Chrome devTools prend en charge la fonctionnalité de fil de travail Workers dans Node.js. worker_threads est un module expérimental. Si vous devez exécuter des opérations gourmandes en CPU dans Node.js, il n'est actuellement pas recommandé d'utiliser des threads de travail dans un environnement de production. Vous pouvez utiliser un pool de processus à la place.

Adresse originale en anglais : https://nodesource.com/blog/worker-threads-nodejs

Auteur : Liz Parody

Plus de programmation pour connaissances connexes, veuillez visiter : Vidéo de programmation ! !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer