محتويات الدورة

Node.js / المتقدم: خدمات Node.js موثوقة / الدرس 13

التزامن المحدود وخيوط العمل

اختر بين الإدخال والإخراج غير المتزامن وعمال المعالج وحدد عدد العمليات المتنافسة على الموارد.

وقت القراءة التقريبي: 4 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • اشرح المفاهيم والمفاضلات المتعلقة بموضوع «التزامن المحدود وخيوط العمل».
  • توقع المثالين وتحقق من مخرجاتهما وسلوك تنظيف الموارد فيهما.
  • شخّص الإخفاقات الثلاثة الشائعة وطبق الحلول المشروحة على مدخلات مشابهة.

مطابقة نموذج التنفيذ للعمل

يسمح الإدخال والإخراج غير المتزامن بتداخل الانتظار دون عامل JavaScript لكل طلب. يظل الحساب الكثيف يشغل خيط العازل ويؤخر الطلبات الأخرى فيه. توفر خيوط العمل تنفيذا منفصلا للحساب داخل العملية، وتوفر العمليات الفرعية حدا منفصلا للعملية. لا يجعل أي منهما الخوارزمية رخيصة. قلل العمل غير اللازم أولا وقس الاختناق. يفيد العامل عادة للحساب الكبير، لا لتغليف كل استدعاء ملفات أو عملية حسابية صغيرة تلقائيا.

تحديد القبول قبل التوسع

يحتاج الطابور إلى حد للعاملين وحد أقصى للانتظار. بدونهما قد تنفد الذاكرة وقت الاندفاع حتى مع ثبات عدد العمال. حدد سلوك الامتلاء: الرفض أو التأجيل أو إسقاط عمل أقل أولوية. يوزع المثال الثاني أربعة عناصر على عاملين غير متزامنين بمؤشر مشترك صغير، ويحفظ الترتيب بوضع النتيجة في موضعها الأصلي. يحد ذلك المهام النشطة، لكن الطابور الفعلي يحتاج أيضا إلى تقييد المصدر بدلا من تخصيص مصفوفة غير محدودة أولا.

التواصل عبر حد صريح

يشغّل المثال الأول ملف .mjs نفسه كخيط رئيسي وعامل، ويفرع عبر isMainThread. تمرر workerData مدخلا صغيرا وتعيد parentPort النتيجة. يراقب الخيط الرئيسي الرسالة وخروج العامل كي لا يترك موردا حيا. تستخدم الرسائل عادة النسخ البنيوي، فلا تُرسل الدوال وموارد العملية كيفما اتفق. قد تتجنب ArrayBuffer القابلة للنقل النسخ لكنها تنقل الملكية وتجعل مخزن المرسل غير صالح. وتتطلب الذاكرة المشتركة مزامنة وبرهانا أقوى للصحة من الرسائل.

javascript مثال كامل: التواصل عبر حد صريح
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';
import { once } from 'node:events';
if (isMainThread) {
  const worker = new Worker(new URL(import.meta.url), { workerData: 100 });
  const exited = once(worker, 'exit');
  const [answer] = await once(worker, 'message');
  const [code] = await exited;
  if (code !== 0) throw new Error('Worker failed');
  console.log(answer);
} else {
  let sum = 0;
  for (let i = 1; i <= workerData; i++) sum += i;
  parentPort.postMessage(sum);
}
الناتج المتوقع
5050

يجمع عامل الأعداد من1 إلى100 ثم يخرج. يوضح العمل الصغير الرسائل ولا يدعي أن بدء عامل يسرعه.

لمجمع العمال سياسة فشل

قد تستهلك تهيئة عامل لكل طلب صغير ونقل البيانات أكثر مما توفره. أعد استخدام مجمع محدود للأعمال الكبيرة المتكررة، وتتبع معرفات المهام وعالج message وerror وexit. عند التعطل حدد المهمة الفاشلة وإمكان تكرارها بأمان. تترك مهلة ترفض المستدعي فقط العامل يحسب؛ ويحتاج الإلغاء إلى بروتوكول تعاوني أو إنهاء مدروس. والعمل غير الموثوق مسألة أمنية مستقلة لأن خيوط العمل تشترك في العملية وليست عزلا أمنيا كاملا.

javascript مثال كامل: لمجمع العمال سياسة فشل
import { setTimeout as delay } from 'node:timers/promises';
const input = [1, 2, 3, 4];
const output = new Array(input.length);
let next = 0;
async function worker() {
  while (next < input.length) {
    const index = next++;
    await delay(1);
    output[index] = input[index] * 2;
  }
}
await Promise.all([worker(), worker()]);
console.log(output.join(','));
الناتج المتوقع
2,4,6,8

تنشط عمليتان غير متزامنتين كحد أقصى. يُحجز المؤشر قبل await فلا يتكرر توزيعه داخل العازل.

التوسع بناء على دليل

قس تأخر الطابور منفصلا عن التنفيذ وأدخل كلفة التسلسل والذاكرة وبدء العامل. قد تزيد العمالة فوق قدرة المعالج تبديل السياق وتسوء الاستجابة. تهم حصص المعالج للحاوية مع عتاد المضيف. حدد المجمع بقياسات ممثلة واترك سعة للحلقة وبيئة التشغيل. اختبر الحمل الزائد وتعطل العامل والإغلاق مع مهام منتظرة والتسليم المكرر. يوضح التصميم الموثوق الملكية والحدود في كل مرحلة بدلا من عد الوعود والخيوط حلا شاملا للأداء.

الأخطاء الشائعة وإصلاحها

تغرق Promise.all اعتمادية

لماذا يحدث؟
بدأت العناصر كلها فورا دون حد قبول.
كيف تصلحه؟
استخدم عاملين محدودين وطابور انتظار محدودا.
كيف تتجنب تكراره؟
قس المهام النشطة وعمق الطابور ومعدل الرفض.

تترك مهلة العامل حسابا مستمرا

لماذا يحدث؟
رُفض وعد المستدعي فقط.
كيف تصلحه؟
نفذ الإلغاء أو أنه العامل واستبدله بقرار واضح.
كيف تتجنب تكراره؟
اختبر المهام غير المنتهية بعد المهلة والإغلاق.

تزيد العمالة زمن الاستجابة

لماذا يحدث؟
تشبعت قدرة المعالج أو كلفة النقل.
كيف تصلحه؟
قلل حجم المجمع وحلل أعمالا ممثلة.
كيف تتجنب تكراره؟
أدخل البدء والتسلسل في القياسات.

اختبر فهمك

1. اختر نموذجا لانتظار عشرة استدعاءات HTTP ولضغط بنية كبيرة كثيفة الحساب.

اعرض الحل المشروح

استخدم إدخالا وإخراجا غير متزامن محدودا للاستدعاءات المنتظرة. ولحساب JavaScript كبير اختبر مجمع عمال بعد مراجعة الواجهات الأصلية والخوارزمية. يتبع الاختيار الاختناق؛ وإضافة خيط لكل انتظار تزيد الكلفة غالبا.

2. يشغّل مجمع أربع مهام وينتظر ثمانيا كحد أقصى. ماذا يحدث للثالثة عشرة المتزامنة؟

اعرض الحل المشروح

السعة النشطة والمنتظرة اثنتا عشرة، فطبق سياسة الحمل الزائد بدلا من إضافة عنصر بلا حد. قد تناسب الواجهة المباشرة استجابة نقص سعة مؤقت. سجل الرفض وتأكد من عدم تكرار العملاء فورا في حلقة سريعة.

أهم ما تعلمته

  • يسمح الإدخال والإخراج غير المتزامن بتداخل الانتظار دون عامل JavaScript لكل طلب.
  • يحتاج الطابور إلى حد للعاملين وحد أقصى للانتظار.
  • قس تأخر الطابور منفصلا عن التنفيذ وأدخل كلفة التسلسل والذاكرة وبدء العامل.

المراجع الرسمية