كيف يمكنك إغلاق التطبيق عندما يكون بعض Waithandle في منتصف مكالمة إلى WiTeOne؟
-
28-09-2019 - |
سؤال
هل هناك طريقة قياسية لإغلاق تطبيق "نظيف" بينما البعض WaitHandle
قد تكون الكائنات في حالة دعوة حظر حالية إلى WaitOne
?
على سبيل المثال ، قد يكون هناك موضوع خلفية يدور بطريقة كهذه:
while (_request.WaitOne())
{
try
{
_workItem.Invoke();
}
finally
{
OnWorkCompleted();
}
}
لا أرى أي طريقة واضحة للتخلص من هذا الموضوع دون الاتصال Thread.Abort
(الذي لا مثبط له). الدعوة Close
على ال _request
كائن (An AutoResetEvent
) ، ومع ذلك ، سوف يرمي استثناء.
حاليا ، الخيط الذي يدير هذه الحلقة له IsBackground
تم تعيين الممتلكات إلى true
, ، وهكذا التطبيق يبدو للإغلاق بشكل صحيح. لكن منذ WaitHandle
الأدوات IDisposable
, ، لست متأكدًا مما إذا كان هذا يعتبر كوشير أو إذا كان يجب التخلص من هذا الكائن بالفعل قبل خروج التطبيق.
هل هذا تصميم سيء؟ إذا لم يكن كذلك ، كيف يتم التعامل مع هذا السيناريو عادة؟
المحلول
تحديد إضافي WaitHandle
اتصل _terminate
سيشير ذلك إلى طلب لإنهاء الحلقة ثم استخدامه WaitHandle.WaitAny
بدلاً من WaitHandle.WaitOne
.
var handles = { _request, _terminate };
while (WaitHandle.WaitAny(handles) == 0)
{
try
{
_workItem.Invoke();
}
finally
{
OnCompleteWork();
}
}
نصائح أخرى
عندما يكون الخيط يحظر (بغض النظر عن ما يحظره) يمكنك الاتصال Thread.Interrupt()
هذا سوف يسبب الاستثناء ThreadInterruptedException
(أعتقد ، قد يكون الأمر مختلفًا بعض الشيء) يمكنك التعامل مع هذا الاستثناء على الخيط نفسه والقيام بأي تنظيف متاح.
تجدر الإشارة إلى أن الخيط سوف يرمي فقط ThreadInterruptedException
عندما يكون الحظر ، إذا لم يتم حظره ، فلن يتم إلقاؤه حتى يحاول حظره التالي.
هذه هي الطريقة "الآمنة" لإنهاء الخيوط مما قرأته حول هذا الموضوع.
تجدر الإشارة أيضًا إلى: إذا قام الكائن بتنفيذ كل من idisposable و finializer (الذي سيفعله إذا كان يستخدم موارد غير مُدارة) ، فسيتصل GC بـ Finalizer الذي يستدعي عادة التخلص منه. عادة هذا غير محدد. ومع ذلك ، يمكنك ضمان أن يتم استدعاء مخرج الطلب. فقط في ظروف خاصة جدا لم يفعلوا ذلك. (بيئة .NET تنهي استثناء مثل StackOverflowException
هذا خطئ)
تعيين IsBackground
خاصية ل true
... يجب أن تغلق الخيط تلقائيًا عند انتهاء تطبيقك.
بالتناوب ، يمكنك مقاطعة الخيط عن طريق الاتصال Thread.Interrupt
والتعامل مع ThreadInterruptedException
. فكرة أخرى هي الاتصال _request.Set()
وجعل الحلقة أثناء حلقة تحقق من علامة متطايرة لتحديد ما إذا كان التطبيق يغلق أو إذا كان يجب أن يستمر:
private volatile bool _running = true;
while(_request.WaitOne() && _running)
{
//...
}
// somewhere else in the app
_running = false;
_request.Set();
أعتقد أن نظام التشغيل سيتم تنظيفه بعد انتهاء العملية. نظرًا لأن خيطك يتم تمييزه كما isBackground ، فإن CLR سينهي العملية وجميع المواضيع في الداخل ، لذلك ليست هذه مشكلة.