2013-06-21 19 views
8

Ich habe eine Warteschlange, die Objekte in einer while-Schleife verarbeitet. Sie werden asynchron irgendwo hinzugefügt .. wie folgt aus:Entwurfsmuster für dynamisches C# -Objekt

myqueue.pushback(String value); 

Und sie werden wie folgt verarbeitet:

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

Nun, die Sache ist, dass ich das ändern möchte einen TTL-like zu unterstützen (time to live), so dass der Dateipfad mehr als n mal hinzugefügt wird.

Wie könnte ich dies tun, während die Funktion Signatur beibehalten? Ich möchte das nicht ändern.

Ich dachte über das Halten einer Karte oder einer Liste, die zählt, wie oft die Prozessfunktion für einen Pfad false zurückgab und den Pfad von der Liste bei der n-ten Rückgabe von false löschte. Ich frage mich, wie dies dynamischer durchgeführt werden kann, und vorzugsweise möchte ich, dass sich die TTL automatisch bei jeder neuen Erweiterung des Prozesses dekrementiert. Ich hoffe, ich spreche nicht Müll. Vielleicht so etwas wie dieses

class JobData 
{ 
    public string path; 
    public short ttl; 

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

Was denken Sie, ist falsch mit Ihrem 'JobData' Ansatz? –

+0

Gut ist die Sache, dass ich nicht die Prozessfunktion ändern möchte, ich will nur 'JobData' Objekt, das eine implizite Umwandlung in String in C# machen kann und einige Ideen über dynamisch und implizit dekrementieren den Tll-Wert – AlexandruC

+0

Eine mögliche zusätzliche Die Lösung, die ich erwähnen, aber nicht befürworten werde, ist das Hinzufügen einer Erweiterungsmethode zum String-Typ mit Ihrem TTL-Zähler. Nicht ideal oder empfohlen, weil es eine sinnlose Methode zum Stringen überall hinzufügt (sogar begrenzt auf einen sehr lokalen Namensraum), aber es würde Ihr spezifisches Problem lösen. –

Antwort

2

Ich mag die Idee einer JobData-Klasse, aber es gibt bereits eine Antwort, die das demonstriert, und die Tatsache, dass Sie mit Dateipfaden arbeiten, gibt Ihnen einen weiteren möglichen Vorteil. Bestimmte Zeichen sind in Dateipfaden nicht gültig, und Sie können daher einen als Trennzeichen verwenden. Der Vorteil hierbei ist, dass der Warteschlangentyp eine Zeichenfolge bleibt und Sie daher keinen der vorhandenen asynchronen Codes ändern müssen. Sie können eine Liste der reservierten Pfad Zeichen sehen hier:

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

Für unsere Zwecke werde ich den Prozentsatz (%) Zeichen. Dann können Sie Ihren Code wie folgt ändern, und es muss nichts anderes ändern:

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

Wieder von einem reinen Architektur Standpunkt, eine Klasse mit zwei Eigenschaften ist ein besseres Design ... aber von einem praktischen Standpunkt, YAGNI : Diese Option bedeutet, dass Sie vermeiden können, anderen asynchronen Code, der in die Warteschlange eindringt, zu ändern. Dieser Code muss immer noch nur über die Strings Bescheid wissen und wird mit diesem unmodifiziert arbeiten.

Eine weitere Sache. Ich möchte darauf hinweisen, dass dies eine ziemlich enge Schleife ist, die dazu neigt, mit einem CPU-Kern wegzulaufen. Wenn dies der Warteschlangentyp ".Net" ist und Ihre Tight-Schleife vor Ihren asynchronen Produktionen steht, um die Warteschlange zu leeren, werden Sie eine Ausnahme auslösen, die aus dem While-Block (true) ausbrechen würde.Sie können beide Probleme mit Code wie folgt lösen:

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

Sie könnten abstrakt/kapseln die Funktionalität des „Jobmanager“ verwenden. Blenden Sie die Warteschlange und die Implementierung vor dem Anrufer aus, damit Sie tun können, was immer Sie möchten, ohne dass die Anrufer sich darum kümmern. Etwas wie dieses:

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

} 

Dann kann Ihre Verarbeitungsschleife den TTL-Wert behandeln.

Bearbeiten - Eine einfache Verarbeitungsschleife hinzugefügt. Dieser Code ist nicht Thread-sicher, aber sollte Ihnen hoffentlich eine Idee geben.

+1

Die ursprüngliche Frage lautet: "Wie kann ich das tun, während ich die Funktionssignatur des Bool-Prozesses (String-Pfad) behalte?". Ich bin mir nicht sicher, ob ich sehe, wo der 'Prozess (String path)' in Ihren Code passt ... – Chris

+1

Das wäre immer noch in der Verarbeitungsschleife –

+1

Mit 'Thread.Sleep()' wie dies ist eine schlechte Idee. Einen 'statischen' Konstruktor zu haben, der niemals zurückkehrt, ist eine * extrem * schlechte Idee. – svick

2

Wenn die Einschränkung, dass ist bool process(String path) kann dann nicht die Funktionalität in myqueue setzen berührt/geändert werden. Sie können seine öffentlichen Signaturen von void pushback(string path) und string pop() behalten, aber intern können Sie Ihre TTL verfolgen. Sie können die Zeichenfolgenpfade entweder in eine JobData-ähnliche Klasse einbetten, die zur internen Warteschlange hinzugefügt wird, oder Sie können eine sekundäre Dictionary -Verschlüsselung nach Pfad verwenden. Vielleicht ist sogar etwas so einfach wie das Speichern des letzten Pfades pop und wenn der folgende push derselbe Pfad ist, können Sie davon ausgehen, dass es sich um ein zurückgewiesenes/fehlgeschlagenes Element handelt. Außerdem können Sie in Ihrer Methode pop sogar einen Pfad löschen, der zu oft abgelehnt wurde, und intern den nächsten Pfad abrufen, sodass der aufrufende Code sich des Problems nicht bewusst ist.

+0

Angesichts der asynchronen Art des Push-Codes, scheint es eine sehr schlechte Idee zu sein, einen Push mit dem letzten Pop zu vergleichen. –

+0

Ja, das stimmt. Wahrscheinlich am besten, um mit einer Verpackungsdatenstruktur zu bleiben. – tcarvin