Maison  >  Article  >  interface Web  >  Parlons de la façon dont Node.js + worker_threads implémente le multi-threading ? (explication détaillée)

Parlons de la façon dont Node.js + worker_threads implémente le multi-threading ? (explication détaillée)

青灯夜游
青灯夜游avant
2022-02-11 20:07:543920parcourir

Cet article vous fera découvrir le module worker_threads, vous présentera comment utiliser worker_threads pour implémenter le multi-threading dans Node et utiliser worker_threads pour exécuter la séquence de Fibonacci comme exemple pratique. J'espère que cela sera utile à tout le monde !

Parlons de la façon dont Node.js + worker_threads implémente le multi-threading ? (explication détaillée)

Normalement, Node.jsNode.js被认为是单线程。由主线程去按照编码顺序一步步执行程序代码,一旦遇到同步代码阻塞,主线程就会被占用,后续的程序代码的执行都会被卡住。没错Node.js的单线程指的是主线程是"单线程"。

为了解决单线程带来的问题,本文的主角worker_threads出现了。worker_threads首次在Node.js v10.5.0作为实验性功能出现,需要命令行带上--experimental-worker才能使用。直到v12.11.0稳定版才能正式使用。

本文将会介绍worker_threads的使用方式,以及利用worker_threads执行斐波那契数列作为实践例子。

先决条件

阅读并食用本文,需要先具备:

  • 安装了 Node.js v12.11.0 及以上版本
  • 掌握 JavaScript 同步和异步编程的基础知识
  • 掌握 Node.js 的工作原理

worker_threads 介绍

worker_threads 模块允许使用并行执行 JavaScript 的线程。

工作线程对于执行 CPU 密集型的 JavaScript 操作很有用。 它们对 I/O 密集型的工作帮助不大。 Node.js 内置的异步 I/O 操作比工作线程更高效。

与 child_process 或 cluster 不同,worker_threads 可以共享内存。 它们通过传输 ArrayBuffer 实例或共享 SharedArrayBuffer 实例来实现。

由于以下特性,worker_threads已被证明是充分利用CPU性能的最佳解决方案:

  • 它们运行具有多个线程的单个进程。

  • 每个线程执行一个事件循环。

  • 每个线程运行单个 JS 引擎实例。

  • 每个线程执行单个 Nodejs 实例。

worker_threads 如何工作

worker_threads通过执行主线程指定的脚本文件来工作。每个线程都在与其他线程隔离的情况下执行。但是,这些线程可以通过消息通道来回传递消息。

主线程使用worker.postMessage()函数使用消息通道,而工作线程使用parentPort.postMessage()函数。

通过官方示例代码加强了解:

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

上述代码主线程工作线程都使用同一份文件作为执行脚本(__filename为当前执行文件路径),通过isMainThread来区分主线程工作线程运行时逻辑。当模块对外暴露方法parseJSAsync被调用时候,都将会衍生子工作线程去执行调用parse函数。

worker_threads 具体使用

在本节使用具体例子介绍worker_threads的使用

创建工作线程脚本文件workerExample.js:

const { workerData, parentPort } = require('worker_threads')
parentPort.postMessage({ welcome: workerData })

创建主线程脚本文件main.js:

const { Worker } = require('worker_threads')

const runWorker = (workerData) => {
    return new Promise((resolve, reject) => {
        // 引入 workerExample.js `工作线程`脚本文件
        const worker = new Worker('./workerExample.js', { workerData });
        worker.on('message', resolve);
        worker.on('error', reject);
        worker.on('exit', (code) => {
            if (code !== 0)
                reject(new Error(`stopped with  ${code} exit code`));
        })
    })
}

const main = async () => {
    const result = await runWorker('hello worker threads')
    console.log(result);
}

main().catch(err => console.error(err))

控制台命令行执行:

node main.js

输出:

{ welcome: 'hello worker threads' }

worker_threads 运算斐波那契数列

在本节中,让我们看一下 CPU 密集型示例,生成斐波那契数列

如果在没有工作线程的情况下完成此任务,则会随着nth期限的增加而阻塞主线程。

创建工作线程脚本文件worker.js

const {parentPort, workerData} = require("worker_threads");

parentPort.postMessage(getFibonacciNumber(workerData.num))

function getFibonacciNumber(num) {
    if (num === 0) {
        return 0;
    }
    else if (num === 1) {
        return 1;
    }
    else {
        return getFibonacciNumber(num - 1) + getFibonacciNumber(num - 2);
    }
}

创建主线程脚本文件main.js:

const {Worker} = require("worker_threads");

let number = 30;

const worker = new Worker("./worker.js", {workerData: {num: number}});

worker.once("message", result => {
    console.log(`${number}th Fibonacci Result: ${result}`);
});

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

worker.on("exit", exitCode => {
    console.log(`It exited with code ${exitCode}`);
})

console.log("Execution in main thread");

控制台命令行执行:

node main.js

输出:

Execution in main thread
30th Fibonacci Result: 832040
It exited with code 0

main.js文件中,我们从类的实例创建一个工作线程,Worker est considéré comme monothread. Le thread principal exécute le code du programme étape par étape selon la séquence de codage. Une fois le code de synchronisation bloqué, le thread principal sera occupé et l'exécution ultérieure du code du programme sera bloquée. C'est vrai, le thread unique de Node.js signifie que le thread principal est "à thread unique".

Afin de résoudre les problèmes causés par un seul thread, le protagoniste de cet article worker_threads apparaît. worker_threads est apparu pour la première fois en tant que fonctionnalité expérimentale dans Node.js v10.5.0 et nécessite --experimental-worker sur la ligne de commande pour l'utiliser. Il ne peut pas être officiellement utilisé avant la version stable v12.11.0.

🎜Cet article expliquera comment utiliser worker_threads et comment utiliser worker_threads pour exécuter Séquence de Fibonacci 🎜 comme exemple pratique. 🎜

Prérequis

🎜Pour lire et utiliser cet article, vous devez avoir : 🎜
  • Installé Node.js v12.11.0 et versions supérieures
  • Maîtriser les connaissances de base de la programmation synchrone et asynchrone JavaScript
  • Maîtriser le principe de fonctionnement de Node.js

Introduction à worker_threads

Introduction à Worker_threads
  • strong>
  • 🎜Le module worker_threads permet l'utilisation de threads qui exécutent du JavaScript dans parallèle. 🎜🎜Les threads de travail sont utiles pour effectuer des opérations JavaScript gourmandes en CPU. Ils ne sont pas très utiles pour les travaux intensifs en E/S. Les opérations d'E/S asynchrones intégrées de Node.js sont plus efficaces que les threads de travail. 🎜🎜Contrairement à child_process ou cluster, worker_threads peut partager la mémoire. Pour ce faire, ils transfèrent des instances ArrayBuffer ou partagent des instances SharedArrayBuffer. 🎜🎜Les worker_threads se sont avérés être la meilleure solution pour utiliser pleinement les performances du processeur grâce aux propriétés suivantes : 🎜
    • 🎜Ils exécuter Un seul processus avec plusieurs threads. 🎜
    • 🎜Chaque thread exécute une boucle d'événement. 🎜
    • 🎜Chaque thread exécute une seule instance du moteur JS. 🎜
    • 🎜Chaque thread exécute une seule instance 🎜Nodejs🎜. 🎜

    Comment fonctionne Worker_threads

    🎜worker_threadsen exécutant le spécifié par le <code>thread principal >fichier de script pour fonctionner. Chaque thread s'exécute indépendamment des autres threads. Cependant, ces fils de discussion peuvent transmettre des messages via des canaux de messagerie. 🎜🎜Main thread utilise la fonction worker.postMessage() pour utiliser le canal de message, tandis que worker thread utilise parentPort.postMessage( ) code> fonction. 🎜🎜Améliorez votre compréhension grâce à l'exemple de code officiel : 🎜
    console.log("Execution in main thread");
    🎜Le code ci-dessus thème principal et les Threads de travail utilisent tous le même fichier que le script d'exécution (__filename est le chemin du fichier d'exécution actuel), et le thread principal se distingue par isMainThread > Logique d'exécution avec threads de travail. Lorsque la méthode parseJSAsync exposée en externe du module est appelée, un thread sous-travailleur sera généré pour exécuter la fonction parse. 🎜

    Utilisation spécifique de Worker_threads

    🎜Dans cette section, nous utilisons des exemples spécifiques pour présenter l'utilisation de worker_threads🎜🎜Créer un worker thread code> fichier de script <code>workerExample.js :🎜rrreee🎜Créer un thread principal fichier de script main.js :🎜rrreee🎜Exécution de la ligne de commande de la console :🎜 rrreee🎜output :🎜rrreee

    worker_threads Operation Fibonacci Sequence

    🎜Dans cette section, regardons un exemple gourmand en CPU, générant
    Nombres de Fibonacci 🎜. 🎜🎜Si cette tâche est terminée sans thread de travail, le thread principal sera bloqué à mesure que le énième délai augmente. 🎜🎜Créez le fichier de script thread de travail worker.js🎜rrreee🎜Créez le fichier de script thread principal main.js : 🎜rrreee🎜Exécution de la ligne de commande de la console : 🎜rrreee🎜Sortie : 🎜rrreee🎜Dans le fichier main.js, nous créons un thread de travail à partir d'une instance de la classe, Worker comme nous l'avons vu dans l'exemple précédent. 🎜🎜Afin d'obtenir les résultats, nous écoutons 3 événements, 🎜
    • message响应工作线程发出消息。
    • exit工作线程停止执行的情况下触发的事件。
    • error发生错误时触发。

    我们在最后一行main.js

    console.log("Execution in main thread");

    通过控制台的输出可得,主线程并没有被斐波那契数列运算执行而阻塞。

    因此,只要在工作线程中处理 CPU 密集型任务,我们就可以继续处理其他任务而不必担心阻塞主线程。

    结论

    Node.js 在处理 CPU 密集型任务时一直因其性能而受到批评。通过有效地解决这些缺点,工作线程的引入提高了 Node.js 的功能。

    有关worker_threads的更多信息,请在此处访问其官方文档。

    思考

    文章结束前留下思考,后续会在评论区做补充,欢迎一起讨论。

    • worker_threads线程空闲时候会被回收吗?
    • worker_threads共享内存如何使用?
    • 既然说到线程,那么应该有线程池?

    更多node相关知识,请访问:nodejs 教程

    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