آلية القفل التي توفر معلومات حول الحالة في .NET 3.5
-
29-09-2019 - |
سؤال
أحاول إيجاد طريقة لتوفير وصول حصري إلى مورد ، بينما في نفس الوقت تقديم معلومات حول حالة القفل (_isworking) لقراءة الفصول الأخرى.
هذا ما توصلت إليه حتى الآن:
private int _isWorking = 0;
public bool IsWorking
{
get { return Interlocked.CompareExchange(ref _isWorking, 0, 0) == 1; }
}
public void LaunchProcess()
{
if (Interlocked.CompareExchange(ref _isWorking, 1, 0) == 0)
{
try
{
DoExpansiveProcess();
}
finally
{
Interlocked.Exchange(ref _isWorking, 0);
}
}
}
public void DoExpansiveProcess()
{
Thread.Sleep(30000);
}
إنه لا يعمل تمامًا كقفل لأن الخيط الثاني ، عند رؤية أن الخيط يعمل بالفعل على العملية ، سيترك الطريقة ولا يفعل شيئًا.
هل هي الطريقة الصحيحة للقيام بذلك أم أن هناك أي بنية C# 3.5 أكثر تكييفًا لهذا الغرض؟
وكيفية تنفيذ سيناريو قفل "حقيقي" ، حيث لا يزال الخيط الثاني ينفذ الطريقة بعد الانتهاء من أول واحد ، مع توفير معلومات حول الحالة؟
المحلول
على افتراض أنني أفهم سؤالك بشكل صحيح ثم أعتقد أن ما أنت عليه هو Monitor.TryEnter
الطريقة ذات قيمة مهلة 0. ستحاول الحصول على القفل ، ولكن مع مهلة 0 ، ستعود دائمًا على الفور بقيمة تشير إلى ما إذا كان قد تم الحصول على القفل بالفعل. المعنى الضمني هنا هو أنه إذا لم يتم الحصول على القفل ، فمن المفترض أنه تم الحصول عليه بالفعل من قبل شخص آخر. المشكلة هي أنه في حالة عودة true
ثم يجب عليك إصدارها فورًا باستخدام مكالمة إلى Monitor.Exit
.
ربما سيكون هذا حلاً أفضل:
public class Worker
{
private Object m_LockObject = new Object();
private volatile bool m_IsWorking = false;
public bool IsWorking
{
get { return m_IsWorking; }
}
public void LaunchProcess()
{
lock (m_LockObject)
{
m_IsWorking = true;
DoExpansiveProcess();
m_IsWorking = false;
}
}
}
نصائح أخرى
System.Threading.ManualResetEvent
قد تكون قادرة على المساعدة هنا. فقط افعل waitone () ضدها. إذا تم استخدامه ، فسوف يعود كخطأ. إذا لم يكن قيد الاستخدام ، فهو متاح.
يستغرق أحد الحمولة الزائدة في Waitone () وقتًا في المللي ثانية للانتظار. إذا كنت تستخدم Waitone (0) ، فسيعود الانتظار على الفور بطريقة غير حظر.
يجب عليك إجراء بعض الأبحاث في بدائل التزامن التي يقدمها .NET:
System.Threading.Monitor lock() { }
System.Threading.ManualResetEvent
System.Threading.AutoResetEvent
System.Threading.Semaphore
System.Threading.Mutex
أعتقد أن نهجك يجب أن يكون صحيحًا بشكل عام ، وسيكون بالتأكيد أسرع بكثير من استخدام ManualSeSetevent (إذا كان له صلة على الإطلاق) - لأن ManualResetEvent يلتف الكامنة وراء البدائية غير المدارة.
لجعل هذا النهج آمنًا ، يجب أن يكون العمل خاصًا.