2011-01-13 14 views
10

Sto creando un servizio Windows che utilizza un FileSystemWatcher per monitorare una particolare cartella per aggiunte di un particolare tipo di file. A causa del divario tra l'evento Created e quando il file è effettivamente pronto per essere manipolato, ho creato un Queue<T> per contenere i nomi di file che devono essere elaborati. Nel gestore eventi Created, l'elemento viene aggiunto alla coda. Quindi, usando un timer, prendo periodicamente il primo oggetto dalla coda e lo elabora. Se l'elaborazione non riesce, l'elemento viene aggiunto nuovamente alla coda, in modo che il servizio possa riprovare a elaborarlo in un secondo momento.Come si aggiunge un articolo in primo piano?

Questo funziona benissimo ma ho riscontrato che ha un effetto collaterale: il primo tentativo di elaborazione di nuovi elementi non avviene finché tutti i vecchi tentativi non sono stati riprovati. Poiché è possibile che la coda contenga molti elementi, vorrei forzare i nuovi elementi in primo piano in modo che vengano elaborati per primi. Ma dal numero Queue<T> documentation, non esiste un modo ovvio per aggiungere un articolo alla parte anteriore della coda.

Suppongo di poter creare una seconda coda per i nuovi articoli ed elaborarla preferenzialmente ma avere una singola coda sembra più semplice.

Quindi c'è un modo semplice per aggiungere un articolo in prima fila?

+5

Se si aggiunge alla parte anteriore e alla coda di una coda, cessa di essere una coda, pedanticamente parlando. – CanSpice

+0

perché non aggiungere i nuovi tentativi nella propria raccolta per essere elaborati in seguito? –

+0

Nella STL si usa una deque (che ho sentito pronunciare "deck" ma non lo so davvero). È una coda a doppio attacco. http://www.cplusplus.com/reference/stl/deque/ Non sembra che ce ne sia uno in System.Collections.Generic, ma potresti scriverlo nel modo che descrivi. –

risposta

25

E 'sorta di sembra si desidera un LinkedList<T>, che ti permette di fare cose come AddFirst(), AddLast(), RemoveFirst() e RemoveLast().

+0

+1 Questa è in realtà una soluzione eccezionale. Doh, perché non ci ho pensato! –

+0

+++ Una grande soluzione davvero. Questo è quello che stavo cercando; una coda che non è davvero una coda. :) – Corin

+2

Come bonus aggiuntivo, LinkedList è doppiamente collegato, quindi tutte le operazioni sopra elencate sono O (1). – pR0Ps

3

Bene, sono d'accordo con CanSpice; Tuttavia, si potrebbe:

var items = queue.ToArray(); 
queue.Clear(); 
queue.Enqueue(newFirstItem); 
foreach(var item in items) 
    queue.Enqueue(item); 

Nasty hack, ma funzionerà;)

Piuttosto si potrebbe pensare di aggiungere una seconda istanza di coda. Questa è la coda di 'priorità' che controlli/esegui prima. Questo sarebbe un po 'più pulito. Potresti anche creare la tua classe di coda per avvolgere tutto in modo piacevole e ordinato come;)

+0

Mi è venuto in mente di svuotare la coda e riempirla, ma ho immaginato un meccanismo molto più brutto di quello che proponete. Tuttavia, una classe di coda personalizzata è intrigante. – Corin

2

Suoni come quello che stai cercando è un Stack - Questo è un buffer LIFO (Last in, first out).

+1

-1, vuole solo alcuni tipi di elementi da inserire all'inizio della coda, non tutti. Quindi una pila è altrettanto difettosa per il suo scopo. –

+0

@ csharptest.net, ho letto la domanda dell'OP tre volte, e non è così che mi esamina. Mi sembra che l'OP * sempre * desideri che i nuovi elementi vengano elaborati prima dei vecchi elementi. –

+0

@Kirk Originariamente lo pensavo anch'io, ma dopo averlo riletto sembra che voglia avere tutti i nuovi elementi prioritari rispetto agli oggetti vecchi (indipendentemente da quanto tempo i vecchi elementi erano in attesa) – Rob

7

Utilizzare semplicemente il metodo Peek nel callback del timer anziché Dequeue. Se l'elaborazione ha esito positivo, quindi deselezionare l'elemento.

1

Suggerirei di utilizzare due code: una per i nuovi articoli e l'altra per i tentativi. Avvolgi entrambe le code in un singolo oggetto che ha la stessa semantica di una coda per quanto riguarda la rimozione, ma ti permette di contrassegnare le cose come andando nella coda Nuova o nella coda Riprova su Inserisci. Qualcosa di simile:

public class DoubleQueue<T> 
{ 
    private Queue<T> NewItems = new Queue<T>(); 
    private Queue<T> RetryItems = new Queue<T>(); 

    public Enqueue(T item, bool isNew) 
    { 
     if (isNew) 
      NewItems.Enqueue(item); 
     else 
      RetryItems.Enqueue(item); 
    } 

    public T Dequeue() 
    { 
     if (NewItems.Count > 0) 
      return NewItems.Dequeue(); 
     else 
      return RetryItems.Dequeue(); 
    } 
} 

Naturalmente, avrete bisogno di avere una proprietà Count che restituisce il numero di elementi in entrambe le code.

Se si dispone di più di due tipi di elementi, è necessario passare a una coda di priorità.

+0

Non ha senso. La coda è in coda. Dovrebbe usare la lista collegata. – zgnilec

+1

Il problema con l'approccio della lista collegata è che non dovrebbe davvero mettere in prima fila tutti i nuovi oggetti, nonostante quello che sta letteralmente chiedendo. Vuole che i nuovi articoli vengano elaborati prima degli articoli che devono essere riprovati. Non vuole che i nuovi oggetti vengano elaborati prima dei nuovi articoli più vecchi. Pertanto, la soluzione corretta è costituita da due code e questo lo avvolge decentemente. –

Problemi correlati