سؤال

يحرر: أود أن أرفع الجنون المؤقتة حتى طرح هذا السؤال، لكنه أمر منطقي في ذلك الوقت (انظر تحرير 2 أدناه).

لمشروع .NET 3.5، لدي نوعان من الموارد (R1 و R2.) أحتاج إلى التحقق من توافر. يمكن أن يكون لكل نوع من نوع الموارد (قل) 10 مثيلات في أي وقت.

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

الآن أحتاج إلى الانتظار على مسرحتين منفصلتين (S1. و S2.) أن تتبع توافر الموارد.

WaitHandle[] waitHandles = new WaitHandle[] { s1, s2 };
int signalledHandle = WaitHandle.WaitAny(waitHandles);

switch (signalledHandle)
{
    case 0:
        // Do stuff
        s1.Release();
    case 1:
        // Do stuff
        s2.Release();
}

هناك مشكلة واحدة مع هذا ولكن. من وثائق MSDN WaitAny:

إذا أصبح أكثر من كائن واحد يشير إليه أثناء المكالمة، فستكون قيمة الإرجاع مؤشر الصفيف للكائن الإشارة باستخدام أصغر قيمة فهرس لجميع الكائنات الإشارة.

هذا يشير إلى أنه من الممكن أن أقوم بتهم كل الإشارات الخاصة بي بحلول 1 بعد الاتصال WaitAny. وبعد لأن signalledHandle سيشير إلى أن S1 قد أشار، سأبدأ في استخدام المورد R1, ، وإصدارها عندما انتهيت. ومع ذلك، لأنني لا أعرف إذا S2. تم الإشارة إلى أم لا، قد يكون عدد التوفر على هذا المورد الآن. إذا حدث هذا 10 مرات، فسيكون الإشارة الخاصة بي بشكل دائم "فارغ" ومورد R2. لن تستخدم على الإطلاق بعد الآن.

ما هي أفضل طريقة للتعامل مع هذا؟ يجب أن أتحول من استخدام درعتين إلى عدادات بسيطة وإشارة تلقائية للإشارة عند تغيير أي عداد؟ هل أفتقد بعض النهج الأنيق؟

تحرير 1:
وفقا ل Ravadre، سيتم تغيير واحد فقط من السماوات في الواقع بعد WaitAny. وبعد يبدو أن تعديل مثاله قليلا تأكيد ذلك، ولكن هل هناك أي شخص يمكن أن يشيرني إلى بعض الوثائق الرسمية التي تحدد هذا؟

تحرير 2:
كنت أفكر في هذا في طريقي إلى المنزل. فقط ثم أدركت أن هذا يجب أن يكون صحيحا WaitAny أن تكون من أي استخدام. لن تقتصر هذه المشكلة على السماوات، ولكن فقط عن أي نوع من كائن المزامنة، وجعل WaitAny عديمة الفائدة عمليا.

هل كانت مفيدة؟

المحلول

إذا فهمت مشكلتك بشكل صحيح، أعتقد أن الحل الخاص بك موافق تماما، وأنت فقط فوق تفسير عرض أسعار MSDN. عند الاتصال WaitHandle.WaitAny() ستحصل على أدنى مؤشر، لكنك سوف قفل على waithandle واحد فقط (الإشارة في هذه الحالة)، تحقق من رمز العينة هذا:


Semaphore s1 = new Semaphore(1, 2);
Semaphore s2 = new Semaphore(1, 2);

WaitHandle[] handles = new WaitHandle[] { s1, s2 };

int x = WaitHandle.WaitAny(handles);

int prevS1 = s1.Release();
int prevS2 = s2.Release();

في هذا السيناريو، prevS1 سوف تكون مساوية 0، لأن الإشارة s1 "لقد انتظرت"، لذلك تم تخفيض عداد إلى 0، في حين prevS2 ستكون مساوية ل 1، لأنه لم يتغير حالة منه لأنه مثيل (Release() تعيد الأسلوب العداد قبل الإفراج، لذلك تعني العودة 1 "كان 1، الآن هو 2").

مورد آخر قد ترغب في إلقاء نظرة عليه: http://www.albahari.com/threading/part2.aspx#_wait_handles.. وبعد على الرغم من أنه ليس مصدرا "رسميا"، أعتقد أنه لا يوجد سبب للعثور عليه غير موثوق به.

نصائح أخرى

لغرضك، عند الاتصال WaitHandle.WaitAny() طريقة النتيجة لا يهم. ما يهم الإشارة إلى WAHITHANDLE، لذلك تحتاج إلى محاولة الحصول على قفل / مزامنة مرة أخرى.

void Main() {
 var semaphoreOne = new SemaphoreSlim(0, 1);
 var semaphoreTwo = new SemaphoreSlim(0, 1);

 ReleaseSemaphoreAfterWhile(semaphoreOne);

 bool firstAccepted;
 bool secondAccepted = false;
 while ((firstAccepted = semaphoreOne.Wait(0)) == false &&
  (secondAccepted = semaphoreTwo.Wait(0)) == false) {
  var waitHandles = new [] {
   semaphoreOne.AvailableWaitHandle, semaphoreTwo.AvailableWaitHandle
  };
  WaitHandle.WaitAny(waitHandles);
  Console.WriteLine("SemaphoreOne Before Lock = " + semaphoreOne.CurrentCount);
  Console.WriteLine("SemaphoreTwo Before Lock = " + semaphoreTwo.CurrentCount);
 }

 if (firstAccepted) {
  Console.WriteLine("semaphore 1 was locked");
 } else if (secondAccepted) {
  Console.WriteLine("semaphore 2 was locked");
 } else {
  throw new InvalidOperationException("no semaphores were signaled");
 }
}

Random rd = new Random();
public void ReleaseSemaphoreAfterWhile(SemaphoreSlim semaphore) {
var sleepWork =(int)rd.Next(100, 1000);
 ThreadPool.QueueUserWorkItem(t => {
  Thread.Sleep(10000 + sleepWork);
  semaphore.Release();
 });
}

هناك مجال لتطبيقات أخرى مع نفس الفكرة / المنطق، ولكن استخدام أثناء الحلقة بهذه الطريقة التي تضمنها أن إشارة واحدة فقط ستحصل على المكتسبة، وإذا لم يكن هناك مجال، فإنه يغلق الخيط حتى يتم الإشارة إلى أي من waithandle - مع مراعاة SemaphoreSlim نموذج .Release() طريقة.

لسوء الحظ (كما هو مذكور في التعليقات)، فإنهم بعض سوء الفهم حول مزامنة مؤشر الترابط في الويب، ولكن يجب أن يساعدك هذا الرمز أعلاه في حل مشكلتك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top