پیاده سازی Multithreading در Node.js

۱۴۰۰-۵-۳ ۰۹:۳۰:۵۵  / زمان مطالعه 5 دقیقه
یکی از امکاناتی که شاید کمتر با اون آشنا بودید استفاده از چند نخی در node.js هست که امیدوارم در این مقاله با نحوه کار آشنا بشید. با من همراه باشید.

معرفی

اکثر توسعه دهندگان جاوا اسکریپت معتقدند Node.js تک رشته ای است، که چندین عملیات رو به روش non-blocking asynchronous callback (پاسخ ناهمگام غیر مسدود کننده) انجام میدهد، اما این دیگر معتبر نیست و از زمان انتشار نسخه 13 Node.js قابلیت Multithreading به نام worker threads به آن اضافه شد.

اگر چه با روش non-blocking asynchronous callback می توان چندین عملیات رو بسیار کارآمد انجام داد، اما عملیات هایی که نیاز به استفاده گسترده از پردازنده مرکزی مانند بلوک رمزگذاری برای پردازش های دیگر دارند، این روش برای آن ها جوابگو نیست و بسیار ضعیف است. Multithreading با جدا کردن وظایف این ضعف را برطرف می کند، و باعث می شود که پردازنده در پس زمینه مشغول پردازش رشته ها باشد و بلاکی در دیگر وظایف و کارهای پردازشگر صورت نگیرد.

پیاده سازی

در Node.js به صورت معمول رشته اصلی تمام عملیات را انجام می دهد. با کمک مثال ، نحوه ایجاد یک رشته دیگر برای پردازش یک عملیات نشان داده می شود.

این مثال دارای دو API است ، API اول عملکرد مربوط به پردازش توسط رشته اصلی را نشان می دهد و API دیگر عملکرد را در یک رشته جداگانه پردازش می کند.

قطعه کد زیر ساختار پردازش توسط رشته اصلی را نشان می دهد.

/*
*  File Name: index.js
*  Description: This is the main thread
*/

const express = require("express");

const app = express();
const port = 3000;

app.get("/", (req, res) => {
    res.send("Process function on main thread.");
});

app.get("/seprate-thread", (req, res) => {
    res.send("Process function on seprate thread.");
});

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`);
});

به عنوان اولین مرحله ، یک تابع را بر روی رشته اصلی اضافه می کنیم و به عنوان مرحله بعدی، همان عملکرد را روی یک رشته دیگر اضافه می کنیم.

تابعی که استفاده می شود getSum خواهد بود که مجموع تجمعی را تا مقدار محدودی که به عنوان آرگومان داده می شود ، برمی گرداند. پس از افزودن تابع getSum به موضوع اصلی ، قطعه کد مانند زیر می شود.

/*
*  File Name: index.js
*  Description: This is the main thread
*/

const express = require("express");

const app = express();
const port = 3000;

const getSum = (limit) => {
    let sum = 0;
    for (let i = 0; i < limit; i++) {
        sum += i;
    }
    return sum;
};
app.get("/", (req, res) => {
    const result = getSum(1000);
    res.send(`Processed function getSum on main thread and 
      result:${result}`);
});
app.get("/seprate-thread", (req, res) => {
    res.send("Process function getSum on seprate thread.");
});
app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`);
});

مرحله بعدی اضافه کردن همان عملکرد در یک رشته دیگر است ، و این می تواند به صورت زیر عمل کند.

وارد کردن ماژول worker thread به رشته اصلی.

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

برای تعریف عملکرد getSum برای اجرای یک موضوع دیگر ، یک فایل دیگر ایجاد می کنیم، seprateThread.js. نمونه ای از ماژول worker thread ایجاد کرده و نام فایل را به پرونده تازه ایجاد شده ارائه دهید.

const seprateThread = new Worker(__dirname + "/seprateThread.js");

اجرای رشته جدید

seprateThread.on("message", (result) => {
       res.send(`Processed function getSum on seprate thread:  ${result}`);
});

ارسال داده به رشته جدید

seprateThread.postMessage(1000);

در آخر، رشته اصلی مانند قطعه کد زیر خواهد بود.

/*
*  File Name: index.js
*  Description: This is the main thread
*/

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

const app = express();
const port = 3000;

const getSum = (limit) => {
    let sum = 0;
    for (let i = 0; i < limit; i++) {
         sum += i;
    }
    return sum;
};
app.get("/", (req, res) => {
const result = getSum(1000);
     res.send(`Processed function getSum on main thread and result: 
     ${result}`);
});

app.get("/seprate-thread", (req, res) => {
     const seprateThread = new Worker(__dirname + "/seprateThread.js");
     seprateThread.on("message", (result) => {
          res.send(`Processed function getSum on seprate thread: 
          ${result}`);
     });
     seprateThread.postMessage(1000);
});

app.listen(port, () => {
     console.log(`Example app listening at http://localhost:${port}`);
});

بنابراین یک رشته جدید از رشته اصلی ایجاد می شود. اجازه دهید تابع getSum را بر روی موضوع تازه ایجاد شده قرار دهیم ، بنابراین این عملکرد را در فایل seprateThread.js تعریف می کنیم. پس از تعریف ، رشته جدید قرار است نتیجه را به رشته اصلی ارسال کند. کد زیر را بررسی کنید.

/*
*  File Name: seprateThread.js
*  Description: This is another thread
*/

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

const getSum = (limit) => {
  let sum = 0;
  for (let i = 0; i < limit; i++) {
    sum += i;
  }
  return sum;
};
parentPort.on("message", (limit) => {
 const result = getSum(limit);
 parentPort.postMessage(result);
});

در مثال بالا می توانید عملکرد () seprateThread.postMessage را که توسط رشته اصلی برای برقراری ارتباط با موضوع فرزند استفاده شده است مشاهده کنید. به همین ترتیب ، والد ()Port.postMessage که توسط موضوع فرزند برای برقراری ارتباط با موضوع اصلی استفاده می شود.

ویژگی ها

  • هر رشته دارای موتورهای V8 جداگانه است.
  • رشته های فرزند می توانند با یکدیگر ارتباط برقرار کنند.
  • رشته های فرزند می توانند همان حافظه را داشته باشند.
  • هنگام شروع رشته جدید می توان مقدار اولیه را به عنوان یک گزینه منتقل کرد.

    نتیجه گیری

    تلاش من برای ارائه این مقاله دادن ایده ای ساده جهت راه اندازی multithreading در Node.js است تا شما با آن آشنا شوید و بتوانید در پروژه های بزرگتر از آن استفاده کنید.
multithreading برنامه نویسی چند_نخی