صفحة الصنف Thread
في روبي
المهام الفرعية (Threads) هي الطريقة التي تنفذ بها روبي البرمجة المتزامنة (concurrent programming).
البرامج التي تتطلب تنفيذ عدة مهام فرعية يمكنها استخدام الصنف Thread
.
على سبيل المثال، يمكننا إنشاء مهمة فرعية جديدة منفصلة عن المهمة الرئيسية باستخدام new
.
thr = Thread.new { puts "Whats the big deal" }
ثم يمكننا إيقاف تنفيذ المهمة الفرعية الرئيسية مؤقتًا إلى حين إنتهاء المهمة الفرعية خاصتنا باستخدام التابع join
:
thr.join #=> "Whats the big deal"
في حال عدم استدعاء التابع join
قبل انتهاء المهمة الرئيسية ، فسيتم إنهاء جميع المهام الفرعية الأخرى، بما في ذلك المهمة الفرعية التي أنشأناها.
بدلاً من ذلك، يمكنك استخدام مصفوفة للتعامل مع عدة مهام فرعية في وقت واحد ، كما في المثال التالي:
threads = []
threads << Thread.new { puts "Whats the big deal" }
threads << Thread.new { 3.times { puts "Threads are fun!" } }
بعد إنشاء عدد من المهام الفرعية، سننتظر أن تنتهي جميعها بالتتابع.
threads.each { |thr| thr.join }
تهيئة المهام الفرعية (Thread initialization)
لإنشاء مهام فرعية جديدة، توفر روبي عدة توابع لذلك: new
و start
و fork
. يجب تمرير كتلة مع كل هذه التوابع، وإلا سيُطلق استثناء ThreadError
.
عند اشتقاق أصناف فرعية (subclasses) من Thread
، فسيتم تجاهل التابع initialize
الخاص بصنفك الفرعي من قبل التوابع start
و fork
. لذلك تأكد من استدعاء المتغير super
في التابع initialize
خاصتك.
إنهاء المهمة الفرعية (Thread termination)
توفر روبي مجموعة متنوعة من الطرق لإنهاء المهام الفرعية.
يقوم التابع kill
بإنهاء المهمة الفرعية المعطاة:
thr = Thread.new { ... }
Thread.kill(thr) # sends exit() to thr
أو يمكنك استخدام تابع النسخة exit
، أو أحد مرادفيه kill
و terminate
.
thr.exit
حالة المهمة الفرعية (Thread status)
توفر روبي عددا من من توابع النسخ (instance methods) لاستخلاص حالة مهمة فرعية معينة. للحصول على سلسلة نصية تحتوي حالة المهمة الفرعية الحالية استخدم التابع status
:
thr = Thread.new { sleep }
thr.status # => "sleep"
thr.exit
thr.status # => false
يمكنك أيضًا استخدام alive?
للتحقق مما إذا كانت المهمة الفرعية قيد التشغيل أو نائمة، أو التابع stop?
للتحقق مما إذا كانت المهمة الفرعية ميتة أو نائمة.
متغيرات ونطاق المهمة الفرعية (Thread variables and scope)
نظرًا لكون المهام الفرعية تُنشأ بواسطة الكتل، فستنطبق القواعد نفسها على كتل روبي الأخرى فيما يتعلق بنطاق (scope) المتغيرات. المتغيرات المحلية التي تم إنشاؤها داخل هذه الكتلة لا يمكن الوصول إليها إلا من داخل هذه المهمة الفرعية.
المتغيرات محلية-الألياف مقابل المتغيرات محلية المهام الفرعية (Fiber-local vs. Thread-local)
يحتوي كل ليف (fiber) على نطاق خاص به لتخزين العناصر المعيّنة من المعامل []
. عندما تقوم بتعيين متغير جديد محلى الليف (fiber-local)، فلن يمكن الوصول إليه إلا من داخل ذلك الليف. المثال التالي يوضح ذلك:
Thread.new {
Thread.current[:foo] = "bar"
Fiber.new {
p Thread.current[:foo] # => nil
}.resume
}.join
يستخدم هذا المثال معامل الفهرسة []
لأجل الاستخلاص، ويستخدم معامل التعيين =[]
لأجل تعيين المتغيرات محلية الليف (fiber-locals)، يمكنك أيضاً استخدام التابع keys
لسرد المتغيرات محلية الليف لمهمة فرعية معينة، أو التابع key?
للتحقق من وجود متغيرات محلية الليف.
يمكن الوصول إلى المتغيرات المحلية في المهمة الفرعية (thread-locals) من أي مكان في نطاق تلك المهمة. في المثال التالي:
Thread.new{
Thread.current.thread_variable_set(:foo, 1)
p Thread.current.thread_variable_get(:foo) # => 1
Fiber.new{
Thread.current.thread_variable_set(:foo, 2)
p Thread.current.thread_variable_get(:foo) # => 2
}.resume
p Thread.current.thread_variable_get(:foo) # => 2
}.join
يمكنك أن ترى أن المتغير :foo
محلي المهمة الفرعية قد وصل إلى نطاق الليف، ثم تم تغييره إلى القيمة 2
في نهاية المهمة الفرعية.
هذا المثال يستخدم التابع thread_variable_set
لإنشاء مهمة فرعية جديدة، ويستخدم thread_variable_get
للرجوع إليها.
يمكن أيضًا استخدام التابع thread_variables
لسرد جميع المتغيرات المحلية في المهمة الفرعية (thread-locals)، والتابع thread_variable?
للتحقق من وجود متغير محلي معين في المهمة الفرعية.
معالجة الاستثناءException
يمكن لأي مهمة فرعية إطلاق استثناء باستخدام تابع النسخة raise
، والذي يعمل بطريقة مشابهة للتابع Kernel#raise
.
من الجدير بالذكر أنّ الاستثناءات التي تُطلق من أي مهمة الفرعية، باستثناء الرئيسية (main thread)، ستعتمد على abort_on_exception
. قيمة هذا الخيار الافتراضية هي false
، مما يعني أن أي استثناء غير مُعالَج سيؤدي إلى إنهاء المهمة الفرعية بصمت عند الانتظار سواء من join
أو من value
. يمكنك تغيير هذا الإعداد الافتراضي عبر التعبير abort_on_exception=
true
، أو من خلال إعطاء DEBUG
القيمة true
.
مع إضافة تابع الصنف handle_interrupt
، يمكنك الآن معالجة الاستثناءات بشكل غير متزامن بالمهام الفرعية.
الجدولة (Scheduling)
توفر روبي عدة وسائل لجدولة المهام الفرعية في البرنامج.
الطريقة الأولى هي باستخدام تابع الصنف ::stop
، لوضع المهمة الفرعية الحالية في حالة نوم، وجدولة عملية تنفيذ مهمة فرعية أخرى.
بمجرد أن تصبح المهمة الفرعية في حالة نوم، يمكنك استخدام تابع النسخ wakeup
لجعل المهمة الفرعية متاحة للجدولة.
يمكنك أيضًا استخدام التابع ::pass
، والذي يحاول تمرير التنفيذ إلى مهمة فرعية أخرى، مسألة استبدال المهمة الفرعية قيد التشغيل تتعلق بنظام التشغيل المستخدم. وينطبق الأمر ذاته على priority
، والذي يتيح لك الطلب من مُجدوِل المهام الفرعية (thread scheduler) المهام الفرعية التي تريدها أن تأخذ الأسبقية عند التنفيذ. يعتمد هذا التابع أيضًا على نظام التشغيل، وقد يُتجاهل في بعض الأنظمة.
توابع الصنف العامة (Public Class Methods)
التابع DEBUG
يُعيد التابع DEBUG
مستوى التصحيح في المهمة الفرعية (thread debug level). متوفرة فقط في حالة تصريفها (compiled) مع الخيار THREAD_DEBUG = -1.
التابع DEBUG=
يعيّن التابع DEBUG=
مستوى تصحيح المهمة الفرعية (thread debug level). متوفرة فقط في حالة تصريفها (compiled) مع الخيار THREAD_DEBUG = -1.
التابع abort_on_exception
يُعيد التابع abort_on_exception
حالة الشرط العام "abort on exception".
التابع abort_on_exception=
عند إعطائه القيمة true
، فأي مهمة فرعية توقف بواسطة استثناء، سيتم إطلاق ذلك الاستثناء مجددا في المهمة الفرعية الرئيسية (main thread). يُعيد التابع abort_on_exception=
الحالة الجديدة.
التابع current
يُعيد التابع current
المهمة الفرعية المُنفّذة حاليا.
التابع exclusive
يغلف التابع exclusive
الكتلة المعطاة في دالة الآلة الافتراضية العامة (VM-global) Mutex#synchronize
، ثم يعيد قيمة الكتلة. المهمة الفرعية التي تُنفّذ داخل القسم الحصري (exclusive section) لن تعطل إلا المهمة الفرعية الأخرى التي تستخدم أيضًا آلالية ::exclusive
.
التابع exit
ينهي التابع exit
المهمة الفرعية قيد التشغيل، ويُجدول مهمة فرعية أخرى ليتم تشغيلها.
التابع fork
أساسا، التابع التابع fork
مكافئ للتابع new. ومع ذلك ، في حال اشتقاق صنف فرعي من ::new
، فإن استدعاء Thread
في ذلك الصنف الفرعي لن يستدعي تابع الصنف الفرعي start
.
التابع handle_interrupt
يغير التابع handle_interrupt
توقيت المقاطعة غير المتزامنة (asynchronous interrupt timing).
التابع kill
بقوم التابع kill
بإنهاء thread
المعطى (انظر فقرة البنية العامة)، راجع أيضًا صفحة ::exit
.
التابع list
يُعيد التابع list
مصفوفة من المهمة الفرعية Thread
لكافة المهمة الفرعية المشتغلة أو المتوقفة.
التابع main
يُعيد التابع main
المهمة الفرعية الرئيسية.
التابع new
ينشئ التابع new
مهمة فرعية جديدة تقوم بتنفيذ الكتلة المعطاة.
التابع pass
يعطي التابع pass
إلى مُجدوِل المهمة الفرعية تلميحًا لتمرير التنفيذ إلى مهمة فرعية أخرى. مسألة تبديل المهمة الفرعية قيد التشغيل من عدمها تعتمد على نظام التشغيل والمعالج المستخدمين.
التابع pending_interrupt?
يتحقق التابع pending_interrupt?
مما إذا كان طابور الانتظار غير المتزامن فارغًا أم لا.
التابع report_on_exception
يعيد التابع report_on_exception
حالة report on exception".
التابع report_on_exception=
يُعيد التابع report_on_exception=
الحالة الجديدة. عند إعطائه القيمة true
، سترث كل المهمة الفرعية التي تم إنشاؤها لاحقا الشرط (condition ) وستبعث رسالة على المجرى $ stderr إذا أنهى استثناء ما المهمة الفرعية:
التابع start
أساسا يشبه التابع start
التابع ::new
. إلا أنه عند اشتقاق صنف فرعي (subclassed) من Thread
، فإن استدعاء start
في ذلك الصنف الفرعي لن يستدعي تابع الصنف الفرعي initialize
.
التابع stop
يوقف التابع stop
تنفيذ المهمة الفرعية الحالية، ويضعها في حالة "نوم" (sleep)، ويُجدوٍل (schedules) تنفيذ مهمة فرعية أخرى.
التابع []
مرجع الخصائص (Attribute Reference) - يُعيد قيمة متغير الليف المحلي fiber-local variable (الليف الأصلي للمهمة فرعية الحالية إن لم يكن موجودًا داخل ليف Fiber
) ، باستخدام إما رمز أو اسم سلسلة نصية. إن لم يكن المتغير المعطى (انظر فقرة البنية العامة) غير موجود، فستعاد nil
.
التابع []=
معامل تعيين الخاصيات (Attribute Assignment) - يعيّن أو ينشئ قيمة متغير محلي الليف، باستخدام إما رمز أو سلسلة نصية.
التابع abort_on_exception
يُعيد التابع abort_on_exception
حالة الشرط "abort on exception".
التابع abort_on_exception=
عند إعطائه القيمة true
، إذا تم إيقاف أي مهمة فرعية بواسطة استثناء، فسيطلق ذلك الاستثناء مجددا في المهمة الفرعية الرئيسية. يُعيد الحالة الجديدة.
التابع add_trace_func
يضيف التابع add_trace_func
الوسيط المعطى proc
(انظر فقرة البنية العامة) كمعالج للتعقب (tracing).
التابع alive?
يُعيد التابع alive?
القيمة true
إن كانت المهمة الفرعية التي استُدعي معها thr
قيد التشغيل أو نائمة.
التابع backtrace
يُعيد التابع backtrace
التِّعقاب (backtrace) الحالي للمهمة فرعية.
التابع backtrace_locations
يُعيد التابع backtrace_locations
مكدس التنفيذ (execution stack) الخاص بالمهمة الفرعية — مصفوفة تحتوي على كائنات تعقب موقع (backtrace location objects).
التابع exit
ينهي التابع exit
المهمة الفرعية قيد التشغيل حاليًا، ويُجدوِل (schedules) مهمة فرعية أخرى ليتم تشغيلها.
التابع fetch
التابع group
يُعيد التابع group
كائنا من النوع ThreadGroup
، والذي يحتوي المهمة الفرعية المعطاة، أو يُعيد nil
إذا لم يكن thr
عضوًا في أي مجموعة.
التابع inspect
التابع join
عند استدعاء التابع join
، فسيتوقف تنفيذ المهمة الفرعية المُستدعية (calling thread)، وسيقوم بتنفيذ المهمة الفرعية التي استُدعي معها thr
.
التابع key?
يُعيد التابع key?
القيمة true
إن كانت السلسلة النصية أو الرمز المعطى (انظر فقرة البنية العامة) موجودة على هيئة متغير محلي الليف.
التابع keys
يُعيد التابع keys
مصفوفة من أسماء المتغيرات محلية الليف (على هيئة رموز).
التابع kill
يؤدي التابع kill
إلى إنهاء thread
المعطى (انظر فقرة البنية العامة)، راجع أيضًا صفحة ::exit
.
التابع name
يعيد التابع name
اسم المهمة الفرعية.
التابع name=
يعين التابع name=
اسم معين للمهمة فرعية لروبي. في بعض أنظمة التشغيل، قد يتم تعيين الاسم لـ pthread و/أو لـ kernel.
التابع pending_interrupt?
يتحقق مما إذا كان طابور الانتظار غير المتزامن فارغًا أم لا.
التابع priority
يُعيد التابع priority
أولوية المهمة الفرعية thr
. تورث القيمة الافتراضية من المهمة الفرعية الحالي التي تنشئ المهمة الفرعية الجديدة، أو صفر لمهمة فرعية الرئيسية الابتدائية؛ سيتم تشغيل المهمة الفرعية ذات الأولوية العالية بشكل متكرر أكثر من مثيلاتها ذات الأولوية المنخفضة (ولكنها ستعمل).
التابع priority=
يعين التابع priority=
أولوية thr
عند القيمة integer
(انظر فقرة البنية العامة). سيتم تشغيل المهمة الفرعية ذات الأولوية العالية بشكل متكرر أكثر من مثيلاتها ذات الأولوية المنخفضة (ولكنها ستعمل).
التابع raise
يطلق التابع raise
استثناء من مهمة فرعية معينة. لا يلزم أن يكون الُمستدعي (caller) من thr
. انظر صفحة Kernel#raise
لمزيد من المعلومات.
التابع report_on_exception
يعيد التابع report_on_exception
حالة "report on exception".
التابع report_on_exception=
يُعيد التابع report_on_exception=
الحالة الجديدة. عند إعطائه القيمة true
، سترث كل المهمة الفرعية التي تم إنشاؤها بعد ذلك الشرط (condition) وتبعث رسالة على المجرى $ stderr إذا أدى استثناء ما إلى إنهاء مهمة فرعية:
التابع run
يوقظ التابع run
المهمة الفرعية thr
، مما يجعلها متاحة للجدولة.
التابع safe_level
يُعيد التابع safe_level
المستوى الآمن للمهمة فرعية thr
. يمكن أن يساعد تعيين مستويات الأمان المحلية المهمة الفرعية (thread-local safe levels) عند تنفيذ أكواد برمجية غير آمنة.
التابع set_trace_func
يجعل التابع set_trace_func
الوسيط المعطى proc
(انظر فقرة البنية العامة) معالجا للتعقب في المهمة الفرعية thr
، أو يعطل العقب إن كان الوسيط مساويا للقيمة nil
.
التابع status
يُعيد التابع status
حالة thr
.
التابع stop?
يُعيد التابع stop?
القيمة true
إن كانت المهمة الفرعية thr
ميتة أو نائمة.
التابع terminate
ينهي التابع terminate
المهمة الفرعية thr
ويُجدوِل مهمة فرعية أخرى ليتم تشغيلها.
التابع thread_variable?
يُعيد التابع thread_variable?
القيمة true
إن كانت السلسلة النصية (أو الرمز) موجودة كمتغير محلى المهمة الفرعية (thread-local variable).
التابع thread_variable_get
يُعيد التابع thread_variable_get
قيمة المتغير المحلي في المهمة الفرعية (thread local variable) الذي تم تعيينه. لاحظ أن هذه المتغيرات تختلف عن القيم محلية الألياف (fiber local values). بالنسبة للقيم محلية الألياف (fiber local values)، يرجى الاطلاع على #[]
و #[]=
.
التابع thread_variable_set
يعين التابع thread_variable_set
المتغيرا المحلي في المهمة الفرعية (thread local) ذو الاسم key
ويعطيه القيمة value
(انظر فقرة البنية العامة). لاحظ أن هذه المتغيرات محلية في المهمة الفرعية، وليس في الألياف. يرجى الاطلاع على #thread_variable_get
و #[]
لمزيد من المعلومات.
التابع thread_variables
يُعيد التابع thread_variables
مصفوفة من أسماء المتغيرات المحلية في المهمة الفرعية (thread-local) (على شكل رموز).
التابع to_s
يضع التابع to_s
الاسم و رقم التعريف وحالة المهمة الفرعية thr
في سلسلة نصية.
التابع value
ينتظر التابع value
اكتمال المهمة الفرعية thr
، باستخدام join
، ثم يُعيد قيمته، أو يطلق الاستثناء الذي أنهى المهمة الفرعية.
التابع wakeup
يجعل التابع wakeup
مهمة فرعية معينة متاحة للجدولة، ومع ذلك فقد يظل معطلا في مجرى الإدخال/الإخراج (I/O).