صفحة الصنف 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)

نظرًا لكون المهام الفرعية تُنشأ بواسطة الكتل، تنطبق القواعد نفسها على كتل روبي الأخرى فيما يتعلقق بمدى المتغيرات. المتغيرات المحلية التي تم إنشاؤها داخل هذه الكتلة لا يمكن الوصول إليها إلا من داخل هذه المهمة الفرعية.

تخزين الألياف المحلي مقابل تخزين المهام الفرعية المحلي

يحتوي كل الليف (fiber) على نطاق خاص به لتخزين #[]. عندما تقوم بتعيين نطاق تخزين جديد مخصوص بالليف (fiber-local)، فلن يمكن الوصول إليه إلا من داخل هذا الليف (Fiber). المثال التالي يوضح ذلك:

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.

من الجدير بالذكر أنّ الاستثناءات التي تُطلق الذي يحدث من أي مهمة الفرعية، باستثناء الرئيسية، ستعتمد على 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).

مصادر