生产者消费模式的时候很多产品

时间:2008-11-17 09:35:57

标签: c# design-patterns events .net-3.5

我有一个生产者 - 消费者模式为一种产品工作。当生产者生产许多产品时,最佳实施是什么?例如,消费者应使用的DataBaseEvent,GuiEvent和ControlEvent。下面的代码显示了一个产品(DataBaseEvent)的模式。是否应将每个事件类型排入自己的队列,或者事件是否应继承可以入队的基类。使用多种事件类型时,可能存在更好的模式吗?

class DataBaseEventArgs : EventArgs
{
    public string textToDB = "";
}

class Consumer
{
    private Producer mProducer = new Producer();
    private Queue<DataBaseEventArgs> mDataBaseEventQueue = new Queue<DataBaseEventArgs>();
    private static EventWaitHandle mDataBaseEventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
    private Thread mDataBaseEventDequeueThread = null;

    public Consumer()
    {
        mDataBaseEventDequeueThread = new Thread(DataBaseDequeueEvent);
        mDataBaseEventDequeueThread.Start();
        mProducer.mDataBaseEventHandler += WhenDataBaseEvent;
    }

    protected void DataBaseDequeueEvent()
    {
        while (true)
        {
            DataBaseEventArgs e;
            lock (((ICollection)mDataBaseEventQueue).SyncRoot)
            {
                if (mDataBaseEventQueue.Count > 0)
                {
                    e = mDataBaseEventQueue.Dequeue();
                }
            }
            // WriteToDatabase(e.textToDB);
            if (mDataBaseEventQueue.Count == 0)
            {
                mDataBaseEventWaitHandle.WaitOne(1000);
                mDataBaseEventWaitHandle.Reset();
            }
        }
    }

    internal void WhenDataBaseEvent(object sender, DataBaseEventArgs e)
    {
        lock (((ICollection)mDataBaseEventQueue).SyncRoot)
        {
            mDataBaseEventQueue.Enqueue(e);
            mDataBaseEventWaitHandle.Set();
        }
    }
}

class Producer
{
    public event EventHandler<DataBaseEventArgs> mDataBaseEventHandler = null;

    public void SendDataBaseEvent()
    {
        if (mDataBaseEventHandler != null)
        {
            DataBaseEventArgs e = new DataBaseEventArgs();
            e.textToDB = "This text will be written to DB";
            mDataBaseEventHandler(this, e);
        }
    }
}

2 个答案:

答案 0 :(得分:4)

如果要主动分离工作,多个队列将非常有用 - 即,为不同类型的事件设置不同的线程/池。如果要共享负载,还有另一种选择 - 使用接口(而不是基类)。基类是好的,但我想不出任何会在接口上强制基类的东西。或者甚至只是代表参加工作!

另外 - 在这种情况下我不确定你需要重置事件;您通常可以使用lock和Monitor.Pulse / Wait来处理生产者/消费者(由于不涉及操作系统对象而只需要管理对象,因此开销较少)。但是,如果代码目前是稳定的,也许可以“保持原样” - 线程很难完成一次,更不用说两次......

但是作为参考,它会是这样的:

while(true) {
    T item;
    lock(lockObj) {
        if(queue.Count == 0) { // empty
            Monitor.Wait(lockObj);
            continue; // ensure there is genuinely something to do
        }
        item = queue.Dequeue();
    }
    // TODO: process item
}
...
void Add(T item) {
    lock(lockObj) {
        queue.Enqueue(item);
        if(queue.Count == 1) { // first
            Monitor.PulseAll(lockObj);
        }
    }
}

(清除队列时记住PulseAll)

答案 1 :(得分:0)

我认为如果它们具有相同的优先级并且将以类似的方式处理,那么将所有请求排队到一个队列会更好。

如果其中一个请求的概率较高,那么您需要一些花哨的优先级队列,或者您可以使用不同的队列。

此外,如果您以不同的方式处理消息,那么没有必要使模式过于复杂。