2013-06-21 12 views
8

Mam kolejkę, która przetwarza obiekty w pętli while. Dodaje się je asynchronicznie gdzieś .. tak:Wzór dla dynamicznego obiektu C#

myqueue.pushback(String value); 

I są one przetwarzane tak:

while(true) 
{ 
    String path = queue.pop(); 
    if(process(path)) 
    { 
     Console.WriteLine("Good!"); 
    } 
    else 
    { 
     queue.pushback(path); 
    } 
} 

Teraz chodzi o to, że chciałbym, aby zmodyfikować ten wspierać TTL-like (czas życia), więc ścieżka pliku zostanie dodana o więcej niż n razy.

Jak mogę to zrobić, zachowując podpis funkcji bool process(String path)? Nie chcę tego modyfikować.

Pomyślałem o trzymaniu mapy lub listy, która policzy ile razy funkcja procesu zwróciła false dla ścieżki i upuściła ścieżkę z listy na n-ty zwrot fałszu. Zastanawiam się, jak można to zrobić bardziej dynamicznie, a najlepiej, aby TTL automatycznie zmniejszał się przy każdym nowym dodaniu do procesu. Mam nadzieję, że nie gadam. Może użyciu coś jak ten

class JobData 
{ 
    public string path; 
    public short ttl; 

    public static implicit operator String(JobData jobData) {jobData.ttl--; return jobData.path;} 
} 
+1

Co sądzisz nie tak z twoim podejściem "JobData"? –

+0

Chodzi o to, że nie chcę modyfikować funkcji procesu, po prostu chcę obiektu "JobData", który jest w stanie wykonać niejawną konwersję do String w języku C# i kilka pomysłów na temat dynamicznego i niejawnego zmniejszania wartości Tll. – AlexandruC

+0

Możliwe dodatkowe rozwiązaniem, które wspomnę, ale nie polecę, jest dodanie metody rozszerzenia do typu String z licznikiem TTL. Nie jest to idealne rozwiązanie lub zalecane, ponieważ dodaje bezsensowną metodę do ciągów na całym świecie (nawet ograniczone do bardzo lokalnej przestrzeni nazw), ale rozwiązałoby to twój konkretny problem. –

Odpowiedz

2

Podoba mi się pomysł klasy JobData, ale istnieje już odpowiedź na to pytanie, a fakt, że pracujesz ze ścieżkami plików, daje ci kolejną możliwą przewagę. Pewne znaki nie są poprawne w ścieżkach plików, więc możesz wybrać jedną z nich jako ogranicznik. Zaletą jest to, że typ kolejki pozostaje ciągiem znaków, więc nie trzeba modyfikować żadnego z istniejących asynchronicznych kodów. Można wyświetlić listę zastrzeżonych znaków ścieżki tutaj:

http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words

Dla naszych celów, użyję (%) znak procent. Następnie można zmodyfikować kod w następujący sposób, i nic innego nie musi się zmienić:

const int startingTTL = 100; 
const string delimiter = "%"; 

while(true) 
{ 
    String[] path = queue.pop().Split(delimiter.ToCharArray()); 
    int ttl = path.Length > 1?--int.Parse(path[1]):startingTTL; 

    if(process(path[0])) 
    { 
     Console.WriteLine("Good!"); 
    } 
    else if (ttl > 0) 
    { 
     queue.pushback(string.Format("{0}{1}{2}", path[0], delimiter,ttl));    
    } 
    else 
    { 
     Console.WriteLine("TTL expired for path: {0}" path[0]); 
    } 
} 

Ponownie, z czystego punktu widzenia architektury, klasa z dwóch właściwości jest lepsza konstrukcja ... ale z praktycznego punktu widzenia, YAGNI : ta opcja oznacza, że ​​można uniknąć powrotu i zmiany innego asynchronicznego kodu, który popycha do kolejki. Ten kod nadal musi tylko wiedzieć o łańcuchach i będzie działać z tym niezmodyfikowanym.

Jeszcze jedno. Chcę zauważyć, że jest to dość ciasna pętla, z którą można uciec z rdzeniem procesora. Dodatkowo, jeśli jest to typ kolejki .NET, a twoja napięta pętla wyprzedza twoję asynchroniczną produkuje, aby opróżnić kolejkę, rzucisz wyjątek, który wyłamałby się z bloku while (true).Można rozwiązać oba problemy z kodem tak:

while(true) 
{ 

    try 
    { 
     String[] path = queue.pop().Split(delimiter.ToCharArray()); 
     int ttl = path.Length > 1?--int.Parse(path[1]):startingTTL; 

     if(process(path[0])) 
     { 
      Console.WriteLine("Good!"); 
     } 
     else if (ttl > 0) 
     { 
      queue.pushback(string.Format("{0}{1}{2}", path[0], delimiter,ttl));    
     } 
     else 
     { 
      Console.WriteLine("TTL expired for path: {0}" path[0]); 
     } 
    } 
    catch(InvalidOperationException ex) 
    { 
     //Queue.Dequeue throws InvalidOperation if the queue is empty... sleep for a bit before trying again 
     Thread.Sleep(100); 
    } 
} 
1

Mogłabyś streszczenie/enkapsulacji funkcjonalności „Menedżer zadań”. Ukryj kolejkę i implementację od dzwoniącego, aby móc robić, co chcesz, bez troski o dzwoniącego. Coś takiego:

public static class JobManager 
{ 
    private static Queue<JobData> _queue; 

    static JobManager() { Task.Factory.StartNew(() => { StartProcessing(); }); } 

    public static void AddJob(string value) 
    { 
     //TODO: validate 

     _queue.Enqueue(new JobData(value)); 
    } 

    private static StartProcessing() 
    { 
     while (true) 
     { 
      if (_queue.Count > 0) 
      { 
       JobData data = _queue.Dequeue(); 
       if (!process(data.Path)) 
       { 
        data.TTL--; 
        if (data.TTL > 0) 
         _queue.Enqueue(data); 
       } 
      } 
      else 
      { 
       Thread.Sleep(1000); 
      } 
     } 
    } 

    private class JobData 
    { 
     public string Path { get; set; } 
     public short TTL { get; set; } 

     public JobData(string value) 
     { 
      this.Path = value; 
      this.TTL = DEFAULT_TTL; 
     } 
    } 

} 

Następnie pętla przetwarzania może obsłużyć wartość TTL.

Edytuj - Dodano prostą pętlę przetwarzania. Ten kod nie jest bezpieczny dla wątków, ale mam nadzieję, że dam ci pomysł.

+1

Pierwotne pytanie brzmi: "Jak mogę to zrobić, zachowując podpis funkcji procesu Bool (String path)?". Nie jestem pewien, widzę, gdzie "proces (ścieżka String)" pasuje do Twojego kodu ... – Chris

+1

To nadal będzie w pętli przetwarzania –

+1

Korzystanie z 'Thread.Sleep()' jak to jest zły pomysł. Posiadanie konstruktora 'statycznego', który nigdy nie zwraca, jest * wyjątkowo * złym pomysłem. – svick

2

Jeśli ograniczeniem jest to, że bool process(String path) nie można dotknąć/zmieniona następnie umieścić funkcjonalność w myqueue. Możesz zachować swoje publiczne podpisy void pushback(string path) i string pop(), ale wewnętrznie możesz śledzić swój TTL. Możesz łączyć ścieżki łańcuchowe w klasę podobną do JobData, która jest dodawana do kolejki wewnętrznej, lub możesz mieć wtyczkę podrzędną Dictionary skasowaną przez ścieżkę. Być może nawet coś tak prostego, jak zapisanie ostatniej ścieżki, a jeśli kolejna push jest tą samą ścieżką, można założyć, że był to element odrzucony/nieudany. Ponadto w metodzie pop można nawet odrzucić ścieżkę, która została odrzucona zbyt wiele razy i wewnętrznie pobrać następną ścieżkę, aby kod wywołujący był błogo nieświadomy problemu.

+0

Biorąc pod uwagę asynchroniczny charakter kodu pchającego, dopasowanie przycisku z ostatnim popem wydaje się bardzo złym pomysłem. –

+0

Tak, to prawda. Prawdopodobnie najlepiej trzymać się struktury danych zawijania. – tcarvin

Powiązane problemy