2011-01-13 16 views
10

Tworzę usługę Windows, która korzysta z FileSystemWatcher do monitorowania określonego folderu w celu dodania określonego typu pliku. Ze względu na lukę między utworzonym zdarzeniem, a plikiem, który jest faktycznie gotowy do manipulacji, utworzyłem kod Queue<T> do przechowywania nazw plików wymagających przetworzenia. W module obsługi zdarzeń Utworzono element jest dodawany do kolejki. Następnie za pomocą timera okresowo chwytam pierwszy element z kolejki i przetwarzam go. Jeśli przetwarzanie nie powiedzie się, element zostanie dodany do kolejki, aby usługa mogła ponowić przetwarzanie później.Jak dodać element do początku kolejki?

To działa dobrze, ale odkryłem, że ma jeden efekt uboczny: pierwsza próba przetworzenia nowych elementów nie nastąpi, dopóki nie zostaną ponownie sprawdzone wszystkie stare elementy ponownej próby. Ponieważ jest możliwe, że kolejka może zawierać wiele elementów, chciałbym wymusić nowe pozycje na początku kolejki, aby były przetwarzane jako pierwsze. Ale z wersji Queue<T> documentation nie ma oczywistego sposobu dodawania elementu z przodu kolejki.

Przypuszczam, że mógłbym utworzyć drugą kolejkę dla nowych przedmiotów i przetworzyć tę preferencję, ale mając jedną kolejkę, wydaje się prostszy.

Czy istnieje łatwy sposób dodania elementu do początku kolejki?

+5

Jeśli dodajesz do przodu i do tyłu kolejki, przestaje być kolejką, pedantycznie rzecz biorąc. – CanSpice

+0

dlaczego nie dodać elementów do ponownej próby do ich własnej kolekcji, aby później je przetworzyć? –

+0

W STL używałbyś deque (które słyszałem, wymawiane jako "deck", ale naprawdę nie wiem). To podwójna kolejka. http://www.cplusplus.com/reference/stl/deque/ Nie wydaje się, że jest taki w System.Collections.Generic, ale możesz napisać własny w sposób, który opisujesz. –

Odpowiedz

25

To niby wygląda chcesz LinkedList<T>, który pozwala robić rzeczy jak AddFirst(), AddLast(), RemoveFirst() i RemoveLast().

+0

+1 To naprawdę świetne rozwiązanie. Doh, dlaczego nie pomyślałem o tym! –

+0

+++ To naprawdę świetne rozwiązanie. Tego właśnie szukałem; kolejka, która tak naprawdę nie jest kolejką. :) – Corin

+2

Jako dodatkowy bonus, LinkedList jest podwójnie powiązany, więc wszystkie operacje wymienione powyżej to O (1). – pR0Ps

3

Cóż, zgadzam się z CanSpice; Jednakże, można:

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

Nasty hack, ale to będzie działać;)

raczej myślisz o dodanie drugiej instancji kolejce. Ten jest kolejką priorytetową, którą najpierw sprawdzasz/uruchamiasz. To byłoby trochę czystsze. Możesz nawet utworzyć własną klasę kolejki, aby wszystko było ładnie i schludnie;)

+0

Zdarzyło mi się, że opróżniłem kolejkę i uzupełniłem ją, ale wyobraziłem sobie mechanizm znacznie brzydszy niż to, co proponujecie. Jednak niestandardowa klasa kolejki jest intrygująca. – Corin

2

Brzmi tak, jakbyś chciał, to jest Stack - To jest bufor LIFO (ostatni w pierwszym wyjściu).

+1

-1, chce tylko, aby określone typy przedmiotów były wstawiane na początku kolejki, a nie wszystkie. Tak więc stos jest tak samo wadliwy dla swojego celu. –

+0

@ csharptest.net, Przeczytałem już trzy razy OP-owskie pytanie i nie tak to mnie skanuje. Wydaje mi się, że OP * zawsze * chce, aby nowe przedmioty zostały przetworzone przed starymi przedmiotami. –

+0

@Kirk Pierwotnie tak myślałem, ale po ponownym przeczytaniu wydaje się, że chce, aby wszystkie nowe przedmioty miały pierwszeństwo przed starymi przedmiotami (bez względu na to, jak długo stare przedmioty czekały). – Rob

7

Po prostu użyj metody Peek w swoim oddzwanianiu timera zamiast Dequeue. Jeśli przetwarzanie powiedzie się, odznacz element.

1

Sugerowałbym użycie dwóch kolejek: jednej dla nowych przedmiotów i jednej dla ponownej próby. Oblewaj obie kolejek w jednym obiekcie, który ma taką samą semantykę jak kolejka, jeśli chodzi o usuwanie, ale umożliwia oznaczanie rzeczy jako przechodzenie do nowej kolejki lub kolejki Retry podczas wstawiania. Coś jak:

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

Oczywiście, trzeba mieć właściwość Count która zwraca liczbę elementów w obu kolejek.

Jeśli masz więcej niż dwa typy przedmiotów, czas przejść na kolejkę priorytetową.

+0

To nie ma sensu. Kolejka jest kolejką. Powinien korzystać z połączonej listy. – zgnilec

+1

Problem z podejściem opartym na listach powiązanych polega na tym, że nie powinien on umieszczać wszystkich nowych przedmiotów na pierwszym planie, pomimo tego, o co proszą dosłownie. Chce, aby nowe przedmioty zostały przetworzone przed przedmiotami, które wymagają ponownej próby. Nie chce, aby nowe przedmioty były przetwarzane przed starszymi nowymi przedmiotami. Dlatego właściwym rozwiązaniem są dwie kolejki i to przykrywa to przyzwoicie. –

Powiązane problemy