2013-06-21 14 views
8

whileループでオブジェクトを処理するキューがあります。彼らはこのように...どこかで非同期に追加されます。動的C#オブジェクトのデザインパターン

myqueue.pushback(String value); 

そして、彼らはこのように処理されます。

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

今、事は私がTTL-などをサポートするために、これを変更したいということです(time to live)フラグを立てるので、ファイルパスはn回以上追加されます。

bool process(String path)ファンクションシグニチャを維持しながら、どうすればいいですか?私はそれを変更したくありません。

マップを保持することを考えたり、プロセス関数がパスに対してfalseを返した回数を数えるリストを考えて、n回目のfalseの戻り時にリストからパスを削除しました。どのようにこれをより動的に行うことができるのだろうかと思っています。そして、TTLがプロセスに新たに追加されるたびに自動的に減少することをお勧めします。私はゴミを話していないことを願っています。 は、多分あなたは抽象/「ジョブマネージャ」の機能をカプセル化でき、この

class JobData 
{ 
    public string path; 
    public short ttl; 

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

あなたの 'JobData'アプローチには何が間違っていると思いますか? –

+0

さて、私は、プロセス関数を変更したくありません。C#のStringへの暗黙的な変換を行うことができる 'JobData'オブジェクトと、動的に暗黙的にtll値を減らしたいのです。 – AlexandruC

+0

私が言及するが、保証しない解決策は、あなたのTTLカウンタでString型に拡張メソッドを追加することです。理想的ではありません。なぜなら、どこにでも文字列に意味のない方法(非常にローカルな名前空間に限られています)を追加するからです。しかし、それはあなたの特定の問題を解決するでしょう。 –

答えて

2

私はJobDataクラスのアイデアが気に入っていますが、それを示す答えはすでにあります。ファイルパスを使って作業しているという事実によって、別の利点が得られます。特定の文字はファイルパスでは有効ではないため、区切り文字として使用する文字を選択できます。ここでの利点は、キュータイプが文字列のままであるため、既存の非同期コードを変更する必要がないことです。私はパーセント(%)文字を使用します、私たちの目的のために

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

:あなたはここで予約パスの文字のリストを見ることができます。そして、次のように、あなたのコードを変更することができ、そして他には何も変更する必要がありません:

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

、純粋なアーキテクチャの観点から、二つの性質を持つクラスが良いデザインです...しかし、実用的な観点から、YAGNI :このオプションは、キューにプッシュする他の非同期コードを元に戻すことを避けることができることを意味します。そのコードはまだ文字列について知る必要があり、これを変更しないで動作します。

もう1つ。私はこれがかなりタイトなループであり、CPUコアを使い果たす傾向があることを指摘したいと思います。さらに、これが.NETのキュータイプで、タイトなループが非同期の生成より先になってキューを空にすると、while(真)ブロックから抜け出す例外がスローされます。あなたは、このようなコードで両方の問題を解決することができます:

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

のようなものを使用しました。発信者からキューと実装を非表示にして、発信者が気にしなくても何でもできます。このようなもの:

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

} 

処理ループでTTL値を処理できます。

編集 - 単純な処理ループを追加しました。このコードはスレッドセーフではありませんが、うまくいけばアイデアを与えるはずです。

+1

元の質問は、 "ブールプロセス(文字列パス)関数の署名を保持しながら、私はこれをどうすればできますか"と述べています。私はあなたのコードで 'process(String path)'がどこに収まっているのかわかりません... – Chris

+1

これはまだ処理ループ内にあるでしょう –

+1

これは 'Thread.Sleep()'を使うのは悪い考えです。決して返さない '静的な 'コンストラクタを持つことは*極端に悪い考えです。 – svick

2

制約がbool process(String path)myqueueに機能を入れた後に変更/触れることができないということであるならば。パブリックシグネチャはvoid pushback(string path)string pop()ですが、内部ではTTLを追跡できます。内部のキューに追加されるJobDataのようなクラスの文字列パスをラップするか、パスによって2番目のDictionaryをキーすることができます。おそらく最後のpop edパスを保存するだけの単純なものであっても、その後のpushが同じパスであれば、それは拒否された/失敗したアイテムと見なすことができます。また、popメソッドでは、拒否されたパスを破棄して内部的に次のパスをフェッチして、呼び出し元のコードが問題を認識しないようにすることもできます。

+0

プッシュコードの非同期性を考えると、プッシュと最後のポップを一致させることは非常に悪い考えです。 –

+0

はい、それは本当です。おそらくラッピングデータ構造に固執するのが最良でしょう。 – tcarvin

関連する問題