2008-11-17 2 views
2

j'ai un modèle producteur-consommateur travaillant pour un seul produit. Quelle est la meilleure mise en œuvre lorsque le producteur produit de nombreux produits? Par exemple un DataBaseEvent, GuiEvent et ControlEvent que le consommateur doit consommer. Le code ci-dessous montre le modèle pour un produit (un DataBaseEvent). Chaque type d'événement doit-il être mis en file d'attente dans sa propre file d'attente ou doit-il hériter d'une classe de base pouvant être mise en file d'attente? Peut-être qu'il existe un meilleur modèle lorsque vous travaillez avec de nombreux types d'événements?modèle producteur-consommateur lorsque de nombreux produits

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); 
     } 
    } 
} 

Répondre

0

Je pense que ce serait mieux enqueue toute la demande d'une file d'attente si elles ont la même priorité et seront traitées de façon similaire.

Si l'une des demandes a une probabilité plus élevée, alors vous avez besoin d'une file d'attente de priorité fantaisie ou vous pouvez utiliser différentes files d'attente. De plus, si vous traitez les messages différemment, il est inutile de trop compliquer le modèle.

4

Plusieurs files d'attente seraient utiles si vous souhaitez séparer activement le travail, c'est-à-dire avoir différents threads/pools pour différents types d'événement. Si vous voulez partager la charge, il existe une autre option - utilisez une interface (plutôt qu'une classe de base). La classe de base est bonne, mais je ne peux pas penser à quoi que ce soit qui pourrait rendre obligatoire une classe de base sur une interface. Ou même juste un délégué au travail à faire!

aussi - Je ne suis pas sûr que vous avez besoin d'un événement de remise à zéro dans ce cas; vous pouvez souvent gérer le producteur/consommateur avec juste le verrouillage et Monitor.Pulse/Wait (qui a moins de frais généraux, car aucun objet OS n'est impliqué - juste des objets gérés). Toutefois, si le code est actuellement stable, peut-être laisser « tel quel » - filetage est assez dur pour obtenir le droit une fois, et encore moins deux fois ...

Mais pour référence, ce serait quelque chose comme:

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); 
     } 
    } 
} 

(et souvenez-vous de PulseAll lors de l'effacement des files d'attente)

+0

Merci pour l'exemple de code de moniteur. Je pense que je vais utiliser cela au lieu de l'EventWaitHandler. – humcfc