【问题标题】:How to remove duplicate items from a queue within a time frame?如何在时间范围内从队列中删除重复项?
【发布时间】:2011-06-24 06:55:40
【问题描述】:

我想以一种有效的方式从队列中删除重复的条目。 队列有一个自定义类,包含 DateTime 和 FullPath 以及其他一些东西

private Queue<MyCustomClass> SharedQueue;

类中的 DateTime 是插入队列时的时间戳。我想使用的逻辑如下:如果 FullPath 在 4 秒窗口内相同(即如果在重复完整路径的 4 秒内添加到队列中),则从队列中删除重复项。我有想要观看的活动,但仍会出现一些重复的活动,这没关系。

我正在使用 c# 2.0 和 FileSystemWatcher 类和一个工作队列。

有很多方法可以做到这一点: 每次添加项目时修剪队列,或者当我处理队列时跳过当前重复项目的处理。

或者我应该使用“全局私有”变量 Dictionary 吗?这样我可以快速搜索吗?还是队列的本地副本?如果有很多文件事件,最好将本地队列限制为 100 个项目?虽然在我的情况下,它“应该”只是在一个文件夹中监控相对较少的文件......但事情总是在变化......

感谢您的帮助。

:编辑:美国东部时间 2 月 10 日 8:54: 因此,据我所知,我决定实施一个很好的简单解决方案。 我不认为我持有 Dict 键太久...

:编辑:美国东部标准时间 2 月 10 日 9:53:已更新,因为我的字典不能包含重复值。

   public void QueueInput(HotSynchUnit.RcdFSWFile rcd)
// start the worker thread when program starts.
// call Terminate.Set() in the programs exit routine or close handler etc.
{
  // lock shared queue
  lock (SharedQueue)
  {
    if (!IsDuplicateQueueInput(rcd))  // only add unique values to queue
    {
      SharedQueue.Enqueue(rcd);
      SomethingToDo.Set();
    }
  }
} // public void QueueInput

private bool IsDuplicateQueueInput(HotSynchUnit.RcdFSWFile rcd)
/* Return true if the object is a duplicate object.
 * Pseudo Code:
 * 
 * isDuplicate = false
 * Lock Dictionary
 * -If lastTimeStamp > 4 seconds ago then       // Optimization: save lastTimeStamp
 *    if Dict.Count > 0 then clear Dictionary
 *    return isDuplicate
 * -If not Dict.TryGetValue(sPath, dtTimeStamp) then
 *    Dict.AddKey()
 * -Else
 *    Compare key timestamp to Currenttime
 *    if key timestamp is <= 4 seconds ago then
 *       IsDuplicate = True
 *
 *    Dict.RemoveKey()
 *    Dict.AddKey()
 * 
 * return isDuplicate
*/
{
  // put real code here
}

【问题讨论】:

  • 为什么是“全球私有”字典?为什么不把它和你的队列放在同一个范围内呢?
  • 实际上就是这样。我的错。
  • 感谢@StevenJeuris 在我入队之前检查的想法 - 我忘记了我在那里有那个例程。我不想覆盖或扩展类...

标签: c# queue filesystemwatcher no-duplicates


【解决方案1】:

我会创建一个子类:

class MyDeduplicatedQueue : Queue<MyCustomObject> {
    /// etc
}

然后你可以将所有合适的过滤逻辑放入Enqueue方法中。

【讨论】:

    【解决方案2】:

    我会创建一个包装类而不是从队列扩展,因为基本类型 Queue 的用户期望不同的行为。 (当您这样做时,.NET 4.0 中的数据协定甚至可能会报错。)

    在内部,您可以有一个实际的队列来重定向所需的呼叫。 每次 Queue() 调用,您都可以将新元素添加到 Dictionary 中(如果它尚未包含)。在这样做之前,您可以从该字典中清空所有早于 x 秒的元素,并将它们按顺序添加到内部队列中。

    出队时,必须检查内部队列是否包含元素,否则从字典中选择最早的元素。

    这当然只是一种可能的实现方式。当许多不同的元素可能很快排队时,字典会很快填满,可能需要添加额外的逻辑来解决这个问题。

    【讨论】:

    • public void QueueInput(HotSynchUnit.RcdFSWFile rcd) // 程序启动时启动工作线程。 // 在程序退出例程或关闭处理程序等中调用 Terminate.Set()。 { // 锁定共享队列 lock (SharedQueue) { SharedQueue.Enqueue(rcd); SomeToDo.Set(); } } // 公共无效队列输入
    • 是的,看起来是这样。就像我说的,不同的实现是可能的。当使用本地队列而不是字典时,您会在添加时用查找时间的速度来换取清理早于 x 秒的添加元素的速度。您也可以同时使用两者,以在这两种情况下以内存使用换取速度。 :) 我的建议是,首先写得简单,当它性能不够时,相应地更新解决方案。
    • 好的,谢谢。现在,我将在调用 Enqueu 之前在 QueueInput 例程中使用 Dictionary。为了简单起见,我想我更喜欢使用字典而不是同步队列。我只是将字典计数限制为 1000 行或其他内容?
    • 当今的处理器非常高效。 ;p 1000 个元素应该不会造成太大问题。
    【解决方案3】:

    我只是想使用类似于 通用哈希表任何集合...像这样的东西:

    Dictionary<string, YourClass> dict = new Dictionary<string, YourClass>();
    
    /// just let's assume you want to add/check for "c:\demo.txt"
    
    if (!dict.ContainsKey(@"c:\demo.txt"))
    {
       /// add items to dict by passing fullPath as key and your objects as value
       dict.add(@"c:\demo.txt", obj1);
    } 
    else if (dict[@"c:\demo.txt"].CheckForIntervall())
    {
       /// replace current object in dictionary with new object - in case you want to..
       /// or just do what you want to 
    }
    

    编辑 - 您的自定义类可能具有如下功能:

    class YOURCUSTOMCLASS
    {
        private DateTime creationTime;
    
        public DateTime CreationTime
        { get { return creationTime; } }
    
        public YOURCUSTOMCLASS(parametersGoesHere xyz)
        {
              creationTime = DateTime.Now;
        }
    
        /// in this case this method will return true
        /// if the timeSpan between this object and otherObject
        /// is greater than 4 seconds
        public bool CheckForInterval(YOURCUSTOMCLASS otherObject)
        {
             TimeSpan diff = otherObj.CreationTime.Subtract(creationTime);
    
             /// you may replace 4 through any other digit, or even better take
             /// a const/global var/static ...
             return diff.TotalSeconds > 4;
        }
    
        /// all the other stuff you need ...
    }
    

    当然,您会失去队列的功能 - 但如果您的队列包含许多元素,您将获得运行时间大幅增加

    【讨论】:

    • 您不需要检查密钥是否存在。您可以删除该检查并将 dict.add 设置为 dict[key] = "demo.txt" ,您将得到相同的结果,但更简洁一些。
    • 这就是我基本上已经对字典做的事情,但锁定字典并将其限制为 n 行。我对 CheckForInterval() 以及您将如何做到这一点感兴趣。
    • @philip - 当然你是对的 - 在最初的问题中有一个额外的任务 - 如果给定的时间间隔超时,可以添加/替换现有对象。 @user610064 我会在几分钟内进一步调查并回复
    【解决方案4】:

    如果插入有重复的路径,为什么不直接拒绝呢?您所要做的就是从队列尾部开始线性搜索,并在您找到重复项(并拒绝插入)或时间戳超过您的时间限制(并插入记录)时停止?似乎比保留另一个数据结构和所有相关逻辑要简单得多。

    【讨论】:

    • 怎么样?您是否建议以某种方式使用 Queue.Contains(..)?
    猜你喜欢
    • 2018-12-11
    • 2020-03-02
    • 2023-01-16
    • 2018-04-30
    • 2017-08-22
    • 2015-02-01
    • 2022-01-03
    • 2023-01-10
    • 1970-01-01
    相关资源
    最近更新 更多