8

我有一个在 while 循环中处理对象的队列。它们在某处异步添加..像这样:

myqueue.pushback(String value);

它们的处理方式如下:

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

现在,问题是我想修改它以支持类似 TTL(生存时间)的标志,因此文件路径将被添加超过 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;}
}
4

3 回答 3

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:这个选项意味着您可以避免返回并更改其他推送到队列中的异步代码。该代码仍然只需要了解字符串,并且无需修改即可使用。

还有一件事。我想指出,这是一个相当紧凑的循环,很容易被 cpu 核心跑掉。此外,如果这是 .Net 队列类型,并且您的紧密循环领先于异步生成以清空队列,您将引发异常,这将打破 while(true) 块。您可以使用以下代码解决这两个问题:

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);
    }
}
于 2013-06-21T14:18:45.210 回答
2

如果约束是bool process(String path)无法触摸/更改的,则将功能放入myqueue. 您可以保留 和 的公共签名void pushback(string path)string pop()但在内部您可以跟踪您的 TTL。您可以将字符串路径包装在 JobData添加到内部队列中的类似类中,也可以使用Dictionary路径作为辅助键。甚至可能像保存最后一个poped 路径这样简单,如果后续push路径相同,您可以假设它是被拒绝/失败的项目。此外,在您的pop方法中,您甚至可以丢弃已被拒绝太多次的路径并在内部获取下一条路径,因此调用代码完全没有意识到该问题。

于 2013-06-21T14:44:52.757 回答
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 值。

编辑 - 添加了一个简单的处理循环。这段代码不是线程安全的,但希望能给你一个想法。

于 2013-06-21T13:23:02.377 回答