وحدة الخيوط العاملة (Worker Threads) في Node.js
مؤشر الاستقرار: 1 - تجريبي
توفر وحدة worker
طريقة لإنشاء بيئات متعددة تعمل علي خيوط مستقلة، ولإنشاء قنوات رسائل بينها. ويمكن الوصول إليها باستخدام الراية --experimental-worker
flag بالإضافة إلى:
const worker = require('worker_threads');
وتفيد الخيوط العاملة (Workers) في أداء عمليات JavaScript كثيفة الاستخدام لوحدة المعالجة المركزية؛ ويجب ألَّا تستخدم في عمليات الإدخال والإخراج I/O، إذ تتعامل آلياتُ Node.js المدمجة لتنفيذ العمليات بشكل غير متزامن معها بشكل أكثر كفاءة من خيوط Worker.
علي عكس العمليات التابعة أو عند استخدام وحدة cluster
، يمكن أيضًا أن تشارك الخيوطُ العاملةُ الذاكرةَ بكفاءة عن طريق نقل مثيلات ArrayBuffer
أو مثيلات sharingSharedArrayBuffer
فيما بينهما.
const {
Worker, isMainThread, parentPort, workerData
} = require('worker_threads');
if (isMainThread) {
module.exports = async 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 لكل استدعاء تحليل parse
. ومن الناحية العملية، يوصي بشدة باستخدام مجموعة من الخيوط العاملة (Workers) لهذه الأنواع من المهام، لان العبء المُحمَّل لإنشاء Workers قد يتجاوز على الأرجح الفائدة من تسليم العمل إليها.
worker.isMainThread
أضيف مع الإصدار: v10.5.0.
- من النوع
<boolean>
.
ويكون true
إذا لم تكن هذه الشيفرة تعمل داخل خيط Worker
.
worker.parentPort
أضيف مع الإصدار: v10.5.0.
- من النوع
<null>
|<MessagePort>
.
إذا نتج هذا الخيط كـ Worker
، سيصبح هذا MessagePort
يسمح بالاتصال مع الخيط الأصل. وستكون الرسائل المرسلة باستخدام parentPort.postMessage()
متوفرة في الخيط الأصل باستخدام worker.on('message')
، وستكون الرسائل المرسلة من الخيط الأصل باستخدام worker.postMessage()
متوفرة في هذا الخيط باستخدام parentPort.on('message')
.
worker.threadId
أضيف مع الإصدار: v10.5.0.
من النوع <integer>
.
عدد صحيح مُعرِّف للخيط الحالي. إذا توفَّر كائن العامل المطابق، فانه يتوفر كـ worker.threadId
.
worker.workerData
أضيف مع الإصدار: v10.5.0.
قيمة JavaScript عشوائية تحتوي علي نسخة من البيانات التي مُرِرت إلى مُنشئ خيط Worker
هذا.
الصنف MessageChannel
أضيف مع الإصدار: v10.5.0.
تُمثل مثيلات الصنف worker.MessageChannel
قناة اتصالات غير متزامنة، ثنائية الاتجاه. ليس لدي MessageChannel
توابع خاصة بها. ويُعطي التابعُ MessageChannel()
الجديد كائنًا له الخصائص port1
و port2
، والتي تشير إلى مثيلات MessagePort
.
const { MessageChannel } = require('worker_threads');
const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log('received', message));
port2.postMessage({ foo: 'bar' });
// طباعة: received { foo: 'bar' } from the `port1.on('message')` listener
الصنف: MessagePort
أضيف مع الإصدار: v10.5.0.
يمتد: <EventEmitter>
.
تمثل مثيلات الصنف worker.MessagePort
إحدى نهايتي قناة اتصالات غير متزامنة، ثنائية الاتجاه. ويمكن استخدامها لنقل البيانات المُهيكَلة، ومناطق الذاكرة و MessagePorts
أخرى بين مختلف الـ Workers
.
مع استثناء MessagePorts
كونه EventEmitters
بدلًا من EventTargets
، ويطابق هذا التطبيق MessagePorts
الخاص بالمتصفح.
الحدث 'close'
أضيف مع الإصدار: v10.5.0.
ينطلق الحدث 'close'
بمجرد قطع الاتصال بأي من طرفي القناة.
الحدث 'message'
أضيف مع الإصدار: v10.5.0.
value
من أي نوع من الأنواع <any>. وهو القيمة المرسلة.
ينطلق الحدث 'message'
لأي رسالة واردة، تحتوي علي مُدخَل التابع port.postMessage()
المُستنسَخ.
سيتلقى المستمعون علي هذا الحدث نسخة من معامل القيمة كما مُرِر إلى التابع postMessage()
ولا توجد وسائط إضافية.
port.close()
أضيف مع الإصدار: v10.5.0.
يعطل إرسال المزيد من الرسائل علي أيٍ من طرفي الاتصال. يمكن استدعاء هذا التابع بمجرد معرفة انه لن يحدث المزيد من الاتصالات عبر MessagePort
هذا.
port.postMessage(value[, transferList])
أضيف مع الإصدار: v10.5.0.
value
من أي نوع من الأنواع<any>
.transferList
من النوع <Object[]>
.
إرسال قيمة JavaScript إلى الجانب المتلقي من هذه القناة. ستتنقل القيمة بطريقة متوافقة مع خوارزمية استنساخ HTML المُهيكلة. ولا سيما أنها قد تحتوي على مراجع دورية وكائنات مثل مصفوفات مكتوبة والتي لا تستطيع واجهات تطبيقات JSON
تحويلها إلى سلسلة نصية.
transferList
قد تكون قائمة من كائنات ArrayBuffer
و MessagePort
. ولن تكون قابلة للاستخدام علي الجانب المرسل من القناة بعد النقل (حتى لو لم تكن موجودة في value
). وخلافًا للعمليات الأبناء، لا يُدعم نقل المعالجات مثل مقابس الشبكة حاليًا.
إذا احتوي value
على مثيلات SharedArrayBuffer
، ستكون هذه متاحة من أيٍ من الطرفين. ولا يمكن إدراجها في transferList
.
قد لا يزال value
يحتوي على مثيلات ArrayBuffer
غير الموجودة في transferList
؛ في هذه الحالة تنسخ الذاكرة الأساسية بدلًا من نقلها.
لان استنساخ الكائن يستخدم خوارزمية الاستنساخ المُهيكَلة، لا يحتفظ بالخصائص غير القابلة للعد، ومُوصِّلات الخاصية (property accessors)، والنماذج الأولية للكائن. علي وجه الخصوص، ستُقرأ كائنات Buffer
على أنها Uint8Arrays
عادية عند الطرف المُتلقِّي.
سيُستنسخ الكائن message فورًا، ويمكن تعديله بعد النشر دون وجود تأثيرات جانبية.
للحصول علي مزيد من المعلومات حول آليات التسلسل وإلغاء التسلسل من وراء هذه الـ API، راجع واجهات تطبيق التسلسل في الوحدة v8.
port.ref()
أضيف مع الإصدار: v10.5.0.
مقابل unref()
. استدعاء ref()
على منفذ سبق إجراء unref
عليه لن يسمح للبرنامج بالانتهاء إذا كان هو المعالج الوحيد المتبقي (السلوك الافتراضي). استدعاء ref()
مرة أخرى على منفذ سبق إجراء ref
عليه لن يكون له أي تأثير.
إذا كان المستمعون متصلين أو أزيلوا باستخدام .on('message')
، سيُطبق على المنفذ التوابع ref()
و unref()
تلقائيًا اعتمادًا علي وجود مستمعين للحدث.
port.start()
أضيف مع الإصدار: v10.5.0.
بدء تلقي الرسائل علي MessagePort
هذا. عند استخدام هذا المنفذ كمُطلِق للحدث، سيُستدعى هذا التابع تلقائيًا بمجرد إرفاق مستمعي الحدث 'message'
.
port.unref()
أضيف مع الإصدار: v10.5.0.
يسمح استدعاء unref()
على منفذ للخيط بالإنهاء إذا كان هو المعالج النشط الوحيد في نظام الأحداث. استدعاء unref()
مرة أخرى على منفذ سبق إجراء unref
عليه لن يكون له أي تأثير.
إذا كان المستمعون متصلين أو أُزيلوا باستخدام .on('message')
، سيُطبق على المنفذ التوابع ref()
و unref()
تلقائيًا اعتمادًا علي وجود مستمعين للحدث.
الصنف Worker
أضيف مع الإصدار: v10.5.0.
يمثل الصنف Worker
خيط تنفيذ JavaScript مستقل. تتوفر معظم واجهات تطبيقات Node.js داخله.
ومن أبرز هذه الاختلافات داخل بيئة Worker:
- قد يُعاد توجيه
process.stdin
وprocess.stdout
وprocess.stderr
بواسطة الخيط الأصل. - تُضبط الخاصية
require('worker_threads').isMainThread
بالقيمةfalse
. - يصبح منفذ الرسالة
require('worker_threads').parentPort
متاحًا، - ولا يوقف التابعُ
process.exit()
البرنامجَ بأكمله، فقط الخيطَ منفردًا، ولا يُتاحprocess.abort()
. - ولا يُتاح
process.chdir()
ولا توابع العمليات التي تضبط مُعرِّفات المجموعة أو المستخدم. process.env
هو مرجع للقراءة فقط إلى متغيرات البيئة.- لا يمكن تعديل
process.title
. - لن تُسلَّم الإشارات من خلال
process.on('...')
. - قد يتوقف التنفيذ عند أي نقطة نتيجة لاستدعاء التابع
worker.terminate()
. - لا يمكن الوصول إلى قنوات IPC من العمليات الأصلية.
وتوجد حاليًا الاختلافات التالية أيضا، إلى أن يتم معالجتها:
- وحده
inspector
غير مُتاحة حتى الآن. - الإضافات الأصلية غير مدعومة حتى الآن.
يمكن إنشاء مثيلات Worker
داخل Worker
آخرين.
مثلما هو الحال مع Web Workers ووحدة cluster
، يمكن تحقيق الاتصالات في اتجاهين من خلال تمرير الرسائل بين الخيوط. يحتوي Worker داخليَا على زوج مُضمَّن من MessagePorts المقترن بالفعل مع بعضه البعض عند إنشاء الكائن Worker
. في حين لا يكون الكائن MessagePort
علي جانب الأصل مُستهدَفًا بشكل مباشر، تكون وظائفه مُستهدَفة من خلال التابع worker.postMessage()
والحدث worker.on('message')
علي الكائن Worker
لخيط الأصل.
لإنشاء قنوات المراسلة المخصصة (والتي يُفضَّل استخدامها عن استخدام القناة العامة الافتراضية لأنها تُسهِّل فصل الاهتمامات)، يمكن للمستخدمين إنشاء كائن MessageChannel
علي أي خيط وتمرير أحد MessagePorts
علي هذه MessageChannel
إلى الخيط الآخر من خلال قناة موجودة من قبل، مثل القناة العامة.
راجع port.postMessage()
للحصول علي مزيد من المعلومات حول كيفية تمرير الرسائل، وماهية نوع قيم JavaScript التي يمكن نقلها بنجاح عبر حاجز الخيط.
const assert = require('assert');
const {
Worker, MessageChannel, MessagePort, isMainThread, parentPort
} = require('worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
const subChannel = new MessageChannel();
worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]);
subChannel.port2.on('message', (value) => {
console.log('received:', value);
});
} else {
parentPort.once('message', (value) => {
assert(value.hereIsYourPort instanceof MessagePort);
value.hereIsYourPort.postMessage('يرسل العامل هذا');
value.hereIsYourPort.close();
});
}
new Worker(filename[, options])
filename
من النوع <string>
، وهو المسار إلى سكريبت Worker الرئيسي. يجب أن يكون إمَّا مسارًا مطلقًا أو مسارًا نسبيًا (أي بالنسبة إلى دليل العمل الحالي) بدءًا من ./
أو ../
. إذا كانت options.eval قيمتها true، تكون هذه سلسلة نصية تحتوي علي شيفرة JavaScript بدلًا من المسار.
options
من النوع <Object>
.
eval
من النوع<boolean>
، إذا كانtrue
، تفسر الوسيط الأول للمُنشئ كسكريبت يُنفَّذ فور اتصال العامل.workerdata
من أي نوع من الأنواع<any>
، أي قيمة JavaScript ستُستنسَخ وتصبح متاحة في الصورةrequire('worker_threads').workerData
. سيحدث الاستنساخ كما هو موضح في خوارزمية استنساخ HTML المُهيكَلة، وسينطلق خطأ إذا كان لا يمكن استنساخ الكائن (علي سبيل المثال لأنه يحتوي علي دوال).stdin
من النوع<boolean>
، إذا ضُبط بالقيمةtrue
، سيوفرworker.stdin
دفق قابل للكتابة والذي تظهر محتوياته كـprocess.stdin
داخل Worker. بشكل افتراضي، لا تتوفر أي بيانات.stdout
من النوع<boolean>
، إذا ضُبط بالقيمةtrue
، لن يُوجَّهworker.stdout
تلقائيًا من خلاله إلىprocess.stdout
في الأصل.stderr
من النوع<boolean>
، إذا ضُبط بالقيمةtrue
، لن يُوجَّهworker.stder
r تلقائيًا من خلاله إلىprocess.stderr
في الأصل.
الحدث 'error'
أضيف مع الإصدار: v10.5.0.
err
من النوع<Error>
.
سينطلق الحدث 'error'
إذا أجرى الخيط العامل استثناءً غير مُلتقَط. في هذه الحالة، سيتم إنهاء العامل.
الحدث 'exit'
أضيف مع الإصدار: v10.5.0.
exitcode
من النوع<integer>
.
ينطلق الحدث 'exit'
بمجرد توقف العامل. إذا أٌُنهي العامل باستدعاء process.exit()
، سيكون المعامل exitCode
هو رمز الإنهاء الذي مُرِر. إذا أُنهي العامل، سيكون المعامل exitCode
قيمته 1
.
الحدث 'message'
أضيف مع الإصدار: v10.5.0.
value
من أي نوع من الأنواع<any>
. وهو القيمة المُرسَلة.
ينطلق الحدث 'message'
عند استدعاء الخيط العامل للتابع require('worker_threads').postMessage()
. راجع الحدث port.on('message')
للحصول علي مزيد من التفاصيل.
الحدث 'online'
أضيف مع الإصدار: v10.5.0.
ينطلق الحدث 'online'
عند بدء الخيط العامل في تنفيذ شيفرة JavaScript البرمجية.
worker.postMessage(value[, transferList])
أضيف مع الإصدار: v10.5.0.
value
من أي نوع من الأنواع<any>
.transferList
من النوع <Object[]>
.
إرسال رسالة للعامل يستلمها من خلال require('worker_threads').parentPort.on('message').
راجع port.postMessage()
للحصول علي مزيد من التفاصيل.
worker.ref()
أضيف مع الإصدار: v10.5.0.
على عكس unref()
، استدعاء ref()
على عامل سبق إجراء unref
عليه لن يسمح للبرنامج بالانتهاء إذا كان هو المُعالج الوحيد المتبقي (السلوك الافتراضي). استدعاء ref()
مرة أخرى على عامل سبق إجراء ref
عليه لن يكون له أي تأثير.
worker.stderr
أضيف مع الإصدار: v10.5.0.
- من النوع
<stream.Readable>
.
وهو دفق قابل للقراءة يحتوي علي البيانات المكتوبة على process.stderr
داخل خيط العامل. إذا لم تُمرر stderr: true
لمُنشِئ Worker
، ستُوجَّه البيانات إلى دفق process.stderr
لخيط الأصل.
worker.stdin
أضيف مع الإصدار: v10.5.0.
- من النوع
<null>
|<stream.Writable>
.
إذا مُررت stdin: true
لمُنشِئ Worker
، يكون ذلك دفق قابل للكتابة. ستُتاح البيانات المكتوبة إلى دفق process.stdin
في الخيط العامل.
worker.stdout
أضيف مع الإصدار: v10.5.0.
- من النوع
<stream.Readable>
.
وهو دفق قابل للقراءة يحتوي علي البيانات المكتوبة على process.stdout
داخل خيط العامل. إذا مُررت stdout: true
لمُنشِئ Worker
، ستوجه البيانات إلى دفق process.stdout
لخيط الأصل.
worker.terminate([callback])
أضيف مع الإصدار: v10.5.0.
callback
من النوع<Function>
.
إيقاف تنفيذ كافة JavaScript في الخيط العامل في أقرب وقت ممكن. callback
هو دالة اختيارية تُستدعى بمجرد معرفة اكتمال هذه العملية.
تحذير: حاليًا، ليست جميع الشيفرات البرمجية الداخلية في Node.js مستعدة لتوقع الإنهاء في نقاط عشوائية في الوقت المناسب وقد يتلف إذا واجه هذا الظرف. وبناءً على لذلك، يجب حاليًا فقط استدعاء .terminate()
إذا كان من المعروف أن الخيط العامل لا يمكنه الوصول إلى الوحدات الأساسية من Node.js غير المعروض في وحدة العامل.
worker.threadId
أضيف مع الإصدار: v10.5.0.
- من النوع
<integer>
.
عدد صحيح مُعرِّف للخيط المشار إليه. داخل الخيط العامل، ويكون متوفرًا في الصورة require('worker_threads').threadId
.
worker.unref()
أضيف مع الإصدار: v10.5.0.
يسمح استدعاء unref()
على عامل للخيط بالإنهاء إذا كان هو المعالج النشط الوحيد في نظام الأحداث. استدعاء unref()
مرة أخرى على عامل سبق إجراء unref
عليه لن يكون له أي تأثير.