صفحة الصنف 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 }
تهيئة المهام الفرعية
لإنشاء مهام فرعية جديدة، توفر روبي عدة توابع لذلك هي: new
و start
و fork
. يجب تمرير كتلة مع كل هذه التوابع، وإلا سيُطلق الاستثناء ThreadError
.
عند اشتقاق أصناف فرعية (subclasses) من Thread
، فسيتم تجاهل التابع initialize
الخاص بصنفك الفرعي من قبل التوابع start
و fork
. لذلك تأكد من استدعاء المتغير super
في التابع initialize
خاصتك.
إنهاء مهمة الفرعية
توفر روبي مجموعة متنوعة من الطرق لإنهاء المهام الفرعية. ينهي (أو يقتل) التابع kill
مثلًا المهمة الفرعية المعطاة:
thr = Thread.new { ... }
Thread.kill(thr) # sends exit() to thr
أو يمكنك استخدام تابع النسخة exit
، أو أحد الأسماء البديلة له وهي kill
و terminate
.
thr.exit
حالة مهمة الفرعية
توفر روبي عددًا من من توابع النسخة (instance methods) لاستخلاص حالة مهمة فرعية معينة. للحصول على سلسلة نصية تحتوي حالة المهمة الفرعية الحالية، استخدم التابع status
:
thr = Thread.new { sleep }
thr.status # => "sleep"
thr.exit
thr.status # => false
يمكنك أيضًا استخدام التابع alive?
للتحقق مما إذا كانت المهمة الفرعية قيد التشغيل أو نائمة، أو التابع stop?
للتحقق مما إذا كانت المهمة الفرعية ميتة أو نائمة.
متغيرات ونطاق مهمة الفرعية
نظرًا لكون المهام الفرعية تُنشأ بواسطة الكتل، فستُطبَّق القواعد نفسها على كتل روبي الأخرى فيما يتعلق بنطاق (scope) المتغيرات. المتغيرات المحلية التي تم إنشاؤها داخل هذه الكتلة لا يمكن الوصول إليها إلا من داخل هذه المهمة الفرعية.
الألياف المحلية مقابل الخيوط المحلية
يحتوي كل ليف (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) إعطاء الأسبقية لمهام فرعية معينة عند التنفيذ. يعتمد هذا التابع أيضًا على نظام التشغيل، وقد يُتجاهَل في بعض الأنظمة.
توابع الصنف العامة
DEBUG
يُعيد مستوى التصحيح في المهمة الفرعية (thread debug level).
DEBUG=
يعيّن عند استدعائه بالشكل DEBUG = num
قيمة مستوى تصحيح المهمة الفرعية(thread debug level) إلى القيمة num
.
abort_on_exception
يُعيد حالة الشرط العام "المقاطعة عند حدوث استثناء؟" (abort on exception).
abort_on_exception=
يضبط عند استدعائه بالشكل abort_on_exception= boolean
حالة الشرط العام "المقاطعة عند حدوث استثناء؟" (abort on exception) إلى القيمة المنطقية boolean
.
current
يُعيد المهمة الفرعية المُنفّذة حاليًّا.
exclusive
يغلف الكتلة المعطاة في دالة الآلة الافتراضية العامة (VM-global)Mutex.synchronize
، ثم يعيد قيمة الكتلة.
exit
ينهي المهمة الفرعية قيد التشغيل، ويُجدول مهمة فرعية أخرى ليتم تشغيلها.
fork
ينشئ عملية فرعية جديدة بشكل مكافئ للتابع new.
handle_interrupt
يغير توقيت المقاطعة غير المتزامنة (asynchronous interrupt timing).
kill
ينهي المهمة الفرعية المعطاة.
list
يُعيد مصفوفة من المهام الفرعية العاملة أو المتوقفة.
main
يُعيد المهمة الفرعية الرئيسية.
new
ينشئ مهمة فرعية جديدة تقوم بتنفيذ الكتلة المعطاة.
pass
يعطي إلى مُجدوِل المهمة الفرعية تلميحًا لتمرير التنفيذ إلى مهمة فرعية أخرى.
pending_interrupt?
يتحقق مما إذا كان طابور الانتظار غير المتزامن فارغًا أم لا.
report_on_exception
يعيد حالة "التبليغ عند الاستثناء" (report on exception).
report_on_exception=
يضبط عند استدعائه بالشكل report_on_exception= boolean
حالة "التبليغ عند الاستثناء" (report on exception).
start
يشبه التابع new
بشكل أساسي إلا أنه عند اشتقاق صنف فرعي (subclassed) من Thread
، فإنَّ استدعاء start
في ذلك الصنف الفرعي لن يستدعي تابع الصنف الفرعيinitialize
.
stop
يوقف تنفيذ المهمة الفرعية الحالية، ويضعها في حالة "نوم" (sleep)، ويُجدوٍل (schedules) تنفيذ مهمة فرعية أخرى.
تزوابع النسخة العامة (Public Instance Methods)
[]
مرجع الخصائص (Attribute Reference) - يُعيد قيمة متغير الليف المحلي fiber-local variable
[]=
معامل تعيين الخاصيات (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
إن كانت المهمة الفرعية التي استُدعي معها قيد التشغيل أو نائمة.
backtrace
يُعيد backtrace
المتعقب (backtrace) الحالي للمهمة فرعية.
backtrace_locations
يُعيد backtrace_locations
مكدس التنفيذ (execution stack) الخاص بالمهمة الفرعية — مصفوفة تحتوي على كائنات تعقب (backtrace location objects).
exit
ينهي exit
المهمة الفرعية قيد التشغيل حاليًا، ويُجدوِل (schedules) مهمة فرعية أخرى ليتم تشغيلها.
group
يُعيد group
كائنا من النوع ThreadGroup
، والذي يحتوي المهمة الفرعية المعطاة.
inspect
يعيد inspect
اسم ورقم تعريف وحالة المهمة الفرعية على هيئة سلسلة نصية.
join
عند استدعاء join
، فسيتوقف تنفيذ المهمة الفرعية المُستدعية (calling thread)، وسيقوم بتنفيذ المهمة الفرعية التي استُدعي معها.
key?
يُعيد key?
القيمة true
إن كانت السلسلة النصية أو الرمز المعطى موجودة على هيئة متغير محلي الليف.
keys
يُعيد keys
مصفوفة من أسماء المتغيرات محلية الليف (على هيئة رموز).
kill
يؤدي kill
إلى إنهاء المهمة الفرعيةالمعطاة.
name
يعيد name
اسم المهمة الفرعية.
name=
يعين name=
اسم معين للمهمة فرعية لروبي.
pending_interrupt?
يتحقق مما إذا كان طابور الانتظار غير المتزامن فارغًا أم لا.
priority
يُعيد priority
أولوية المهمة الفرعية
priority=
يعين priority=
أولوية المهمة فرعية عند القيمة integer.
raise
يطلق raise
استثناء من مهمة فرعية معينة.
report_on_exception
يعيد report_on_exception
حالة "report on exception".
report_on_exception=
يُعيد report_on_exception=
الحالة الجديدة.
run
يوقظ run
المهمة الفرعية، مما يجعلها متاحة للجدولة.
safe_level
يُعيد safe_level
المستوى الآمن للمهمة فرعية.
set_trace_func
يجعل set_trace_func
الوسيط المعطى proc
معالجا للتعقب في المهمة الفرعية.
status
يعيد status
حالة المهمة الفرعية.
stop?
يُعيد stop?
القيمة true
إن كانت المهمة الفرعية ميتة أو نائمة.
terminate
ينهي terminate
المهمة الفرعية ويُجدوِل مهمة فرعية أخرى ليتم تشغيلها.
thread_variable?
يُعيد thread_variable?
القيمة true
إن كانت السلسلة النصية (أو الرمز) موجودة كمتغير محلي المهمة الفرعية (thread-local variable).
thread_variable_get
يُعيد thread_variable_get
قيمة المتغير المحلي في المهمة الفرعية (thread local variable) الذي تم تعيينه.
thread_variable_set
يعين thread_variable_set
المتغيرا المحلي في المهمة الفرعية (thread local) ذو الاسم key
ويعطيه القيمة value
thread_variables
يُعيد thread_variables
مصفوفة من أسماء المتغيرات المحلية في المهمة الفرعية (thread-local) (على شكل رموز).
to_s
يعيد to_s
اسم و رقم تعريف وحالة المهمة الفرعية على شكل سلسلة نصية.
value
ينتظر value
اكتمال المهمة الفرعية ، باستخدام join
، ثم يُعيد قيمتها.
wakeup
يجعل wakeup
مهمة فرعية معينة متاحة للجدولة.