سؤال

أحتاج إلى تنفيذ قائمة انتظار منتج/مستهلك ، ومستهلكين متعددين ضد منتج واحد.

لدي وظيفة دفع تضيف عنصرًا إلى قائمة الانتظار ثم يتحقق من MaxSize. إذا وصلتنا إلى عودة كاذبة ، في كل حالة أخرى تعود صحيح.

في الكود التالي _vector هي قائمةu003CT> ، onSignal يستهلك بشكل أساسي عنصر بطريقة غير متزامنة.

هل ترى مشكلات مع هذا الرمز؟

public bool Push(T message)
{
    bool canEnqueue = true;

    lock (_vector)
    {
        _vector.Add(message);
        if (_vector.Count >= _maxSize)
        {
            canEnqueue = false;
        }
    }

    var onSignal = SignalEvent;
    if (onSignal != null)
    {
        onSignal();
    }

    return canEnqueue;
}
هل كانت مفيدة؟

المحلول

أعلم أنك قلت منتجًا واحدًا ، مستهلكًا متعدد ، لكن من الجدير بالذكر على أي حال: إذا كانت قائمة الانتظار ممتلئة تقريبًا (قل 24 من أصل 25 فتحة) ، فعندئذٍ إذا كان هناك موضوعان Push في الوقت نفسه ، سوف ينتهي بك الأمر إلى تجاوز الحد. إذا كانت هناك فرصة حتى يكون لديك العديد من المنتجين في وقت ما في المستقبل ، فيجب عليك التفكير في صنع Push مكالمة حظر ، وانتظر "متاح" AutoResetEvent الذي يتم الإشارة إليه بعد إخلاء عنصر أو بعد أن يتم إلغاء عنصر بينما لا تزال هناك فتحات متاحة.

القضية المحتملة الأخرى الوحيدة التي أراها هي SignalEvent. أنت لا تظهر لنا تنفيذ ذلك. إذا تم الإعلان عنه public event SignalEventDelegate SignalEvent, ، ثم ستكون على ما يرام لأن المترجم يضيف تلقائيًا SynchronizedAttribute. ومع ذلك، إذا SignalEvent يستخدم مندوب دعم مع add/remove بناء جملة ، ستحتاج إلى توفير قفل الخاص بك للحدث نفسه ، وإلا سيكون من الممكن للمستهلك الانفصال عن الحدث بعد فوات الأوان ولا يزال يتلقى بعض الإشارات بعد ذلك.

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

بخلاف ذلك ، لا أرى أي مشاكل - على الرغم من أن هذا لا يعني أنه لا يوجد أي شيء ، فهذا يعني أنني لم ألاحظ أي شيء.

نصائح أخرى

أكبر مشكلة أراها هناك استخدام List<T> لتنفيذ قائمة انتظار ؛ هناك مشكلات في الأداء في القيام بذلك ، لأن إزالة العنصر الأول يتضمن نسخ جميع البيانات.

أفكار إضافية أنت ترفع الإشارة حتى لو كنت لم يكن إضافة البيانات ، واستخدام الأحداث بحد ذاتها قد يكون لديك مشكلة في الخيوط (هناك بعض حالات الحافة ، حتى عند التقاط القيمة قبل null الاختبار - بالإضافة إلى ذلك ربما أكثر من استخدام Monitor للقيام بالإشارة).

أود أن أتحول إلى Queue<T> التي لن لديها هذه المشكلة - أو أفضل استخدام مثال مسبقًا ؛ علي سبيل المثال إنشاء قائمة انتظار حظر في .NET؟, ، وهو ما تفعله بالضبط ما تناقشه ، ويدعم أي عدد من المنتجين والمستهلكين. يستخدم نهج الحظر ، ولكن نهج "المحاولة" سيكون:

public bool TryEnqueue(T item)
{
    lock (queue)
    {
        if (queue.Count >= maxSize) { return false; }
        queue.Enqueue(item);
        if (queue.Count == 1)
        {
            // wake up any blocked dequeue
            Monitor.PulseAll(queue);
        }
        return true;
    }
}

أخيرًا - لا "تدفع" إلى كومة, ليس قائمة انتظار؟

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