التابع handle_interrupt
الخاص بالصنف Thread
في روبي
يغير التابع handle_interrupt
توقيت التوقيف غير المتزامن (asynchronous interrupt timing).
التوقيف (interrupt ) يعني الحدث غير المتزامن (asynchronous event) والإجراء المقابل raise
و kill
وإشارة المسك signal trap (غير مدعومة حاليًا) وعملية إنهاء المهمة الفرعية الرئيسية (عند إنهاء المهمة الفرعية الرئيسية، فسيتم إنهاء كل المهام الفرعية الأخرى).
يحتوي الوسيط المعطى hash
(انظر فقرة البنية العامة) أزواج على شاكلة ExceptionClass => :TimingSymbol
. حيث ExceptionClass
هو التوقيف (interrupt) المٌعالج من قبل الكتلة المعطاة. أما TimingSymbol
فيمكن أن يكون أحد الرموز التالية:
-
:immediate
- يستدعي التوقيفات على الفور. -
:on_blocking
- يستدعي التوقيفات أثناء الحالةBlockingOperation
. -
:never
- لا يستدعي جميع التوقيفات أبدا.
BlockingOperation
تعني أن العملية ستقوم بتعطيل المهمة الفرعية المستدعية (calling thread)، مثل read
و write
. في إصدار CRuby ، ستكون BlockingOperation
أي عملية تنفذ بدون قفل الآلة الافتراضية العام (Global VM Lock أو اختصارًا GVL).
التوقيفات غير المتزامنة المقنعة (Masked asynchronous interrupts) ستُؤجل إلى حين تمكينها.
هذا التابع مشابه للتعبيرsigprocmask(3)
.
ملحوظة
يصعب استخدام التوقبفات غير المتزامنة. إذا كنت بحاجة إلى إجراء اتصالات بين المهام الفرعية، فالرجاء استخدام طريقة أخرى، مثل الطوابير (Queues)
. وإن كنت مصرّا على استخدام هذا التابع، فاستخدمه، لكن مع فهم عميق لخصائصه.
الاستخدام
في هذا المثال، يمكننا الاحتراز من استثناءات raise
.
باستخدام رمز التوقيت (TimingSymbol) :never
، سيتم تجاهل الاستثناء RuntimeError
دائما في الكتلة الأولى من المهمة الفرعية الرئيسية. في كتلة handle_interrupt
الثانية، يمكن معالجة الاستثناءات RuntimeError
.
th = Thread.new do
Thread.handle_interrupt(RuntimeError => :never) {
begin
# You can write resource allocation code safely.
Thread.handle_interrupt(RuntimeError => :immediate) {
# ...
}
ensure
# You can write resource deallocation code safely.
end
}
end
Thread.pass
# ...
th.raise "stop"
بينما نتجاهل الاستثناء RuntimeError
، سيكون من الآمن كتابة تعليمات تخصيص الموارد (resource allocation). بعد ذلك ستكون كتلة التأمين (ensure block) هي المكان المناسب لتحرير (deallocate) الموارد بأمان.
التحرز من الاستثناء Timeout::Error
في المثال التالي، سنحترز من الاستثناء Timeout::Error
. سيساعد هذا على منع هدر الموارد عند حدوث هذا الاستثناء أثناء كتلة تأمين (ensure clause) عادية. في هذا المثال، سنستعين بالمكتبة القياسية Timeout
فيlib/timeout.rb
.
require 'timeout'
Thread.handle_interrupt(Timeout::Error => :never) {
timeout(10){
# Timeout::Error doesn't occur here
Thread.handle_interrupt(Timeout::Error => :on_blocking) {
# possible to be killed by Timeout::Error
# while blocking operation
}
# Timeout::Error doesn't occur here
}
}
في الجزء الأول من كتلة timeout
، يمكننا الاعتماد على حقيقة أن الاستثناء Timeout::Error
سيتم تجاهله. ثم في كتلة Timeout::Error =>on_blocking
، فأي عملية تُعطل المهمة الفرعية المستدعية (calling thread) ستكون عرضة للاستثناء Timeout::Error
.
إعدادات التحكم في المكدس (Stack control settings)
من الممكن تكديس مستويات متعددة من كتل handle_interrupt
من أجل التحكم في عدة كائنات من النوع ExceptionClass
و TimingSymbol
في وقت واحد.
Thread.handle_interrupt(FooError => :never) {
Thread.handle_interrupt(BarError => :never) {
# FooError and BarError are prohibited.
}
}
الوراثة من ExceptionClass
سيتم أخذ كافة الاستثناءات الموروثة من الوسيط ExceptionClass
بعين الاعتبار.
Thread.handle_interrupt(Exception => :never) {
# all exceptions inherited from Exception are prohibited.
}
البنية العامة
handle_interrupt(hash) { ... } → result of the block
القيمة المُعادة
يعيد handle_interrupt
نتيجة الكتلة المعطاة.
انظر أيضا
- التابع
fork
: أساسا، التابع التابعfork
مكافئ للتابعnew
. - التابع
kill
: بقوم التابعkill
بإنهاءthread
المعطى.