У меня есть модель производитель-потребитель, работающая на один продукт. Как лучше всего реализовывать, когда производитель выпускает много товаров? Например, 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);
}
}
}





Я думаю, что было бы лучше поставить все запросы в одну очередь, если они имеют одинаковый приоритет и будут обрабатываться аналогичным образом.
Если один из запросов имеет более высокую вероятность, то вам либо нужна необычная очередь с приоритетом, либо вы можете использовать другие очереди.
Кроме того, если вы обрабатываете сообщения по-другому, то нет смысла усложнять шаблон.
Несколько очередей были бы полезны, если вы хотите активно разделить работу, т.е. иметь разные потоки / пулы для разных типов событий. Если вы хотите разделить нагрузку, есть другой вариант - использовать интерфейс (а не базовый класс). Базовый класс - это нормально, но я не могу придумать ничего, что могло бы передать базовый класс интерфейсу. Или даже просто делегат по работе!
Также - я не уверен, что вам нужно событие сброса в этом случае; вы часто можете обрабатывать производителя / потребителя с помощью только блокировки и 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 при очистке очередей)
Спасибо за образец кода монитора. Думаю, я буду использовать это вместо EventWaitHandler.