صفحة الصنف 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).
التابع DEBUG=
يعيّن التابع DEBUG=
مستوى تصحيح المهمة الفرعية (thread debug level). م
التابع abort_on_exception
يُعيد التابع abort_on_exception
حالة الشرط العام "abort on exception".
التابع abort_on_exception=
عند إعطائه القيمة true
، فأي مهمة فرعية توقف بواسطة استثناء، سيتم إطلاق ذلك الاستثناء مجددا في المهمة الفرعية الرئيسية (main thread).
التابع current
يُعيد التابع current
المهمة الفرعية المُنفّذة حاليا.
التابع exclusive
يغلف التابع exclusive
الكتلة المعطاة في دالة الآلة الافتراضية العامة (VM-global) Mutex#synchronize
.
التابع exit
ينهي التابع exit
المهمة الفرعية قيد التشغيل، ويُجدول مهمة فرعية أخرى ليتم تشغيلها.
التابع fork
التابع fork
مكافئ للتابع new
.
التابع handle_interrupt
يغير التابع handle_interrupt
توقيت التوقيف غير المتزامن (asynchronous interrupt timing).
التابع kill
بقوم التابع kill
بإنهاء المهمة الفرعية المعطاة.
التابع list
يُعيد التابع list
مصفوفة من المهام الفرعية المشتغلة أو المتوقفة.
التابع 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=
الحالة الجديدة.
التابع start
يشبه التابع start
التابع new
.
التابع stop
يوقف التابع stop
تنفيذ المهمة الفرعية الحالية،
تزوابع النسخة العامة (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
مهمة فرعية معينة متاحة للجدولة.