صفحة الصنف 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 مهمة فرعية معينة متاحة للجدولة.

مصادر