Как удалить повторяющиеся элементы из очереди в течение определенного периода времени?

Я хотел бы удалить повторяющиеся записи из очереди эффективным способом. В очереди есть собственный класс с DateTime, FullPath и несколькими другими вещами.

private Queue<MyCustomClass> SharedQueue;

DateTime в классе - это отметка времени при вставке в очередь. Логика, которую я хотел бы использовать, заключается в следующем: Удалить дубликаты из очереди, если FullPath идентичен в течение 4-секундного окна (то есть, если он добавлен в очередь в течение 4 секунд после дубликата полного пути). У меня есть события, которые я хочу посмотреть, но несколько дубликатов все равно появятся, и это нормально.

Я использую C# 2.0 и класс FileSystemWatcher и рабочую очередь.

Есть несколько способов сделать это: обрезать очередь каждый раз, когда к ней добавляется элемент, или когда я работаю с очередью, пропустить обработку текущего дублированного элемента.

Или я должен использовать глобальную частную переменную Dictionary? Так я могу быстро найти его? или локальная копия очереди? Возможно, лучше всего ограничить локальную очередь до 100 элементов в случае большого количества файловых событий? Хотя в моем случае это "должно быть" только относительно небольшое количество файлов для мониторинга в папке... но все всегда меняется...

Спасибо за любую помощь.

: Изменить: 10 февраля 8:54 EST: Поэтому я решил реализовать хорошее простое решение, насколько я могу судить. Я не думаю, что держу клавиши Dict слишком долго...

: Изменить: 10 февраля 9:53 EST: обновлено, так как мой словарь не может содержать повторяющиеся значения.

   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
}

4 ответа

Решение

Я просто подумал об использовании любой коллекции, похожей на общую хеш-таблицу... Примерно так:

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

Конечно, вы потеряете функциональность очереди, но вы получите значительное увеличение времени выполнения, если ваша очередь содержит много элементов.

НТН

Я бы сделал подкласс:

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

Затем вы можете поместить всю соответствующую логику фильтрации в Enqueue метод.

Почему бы просто не отклонить вставки, если они имеют повторяющиеся пути? Все, что вам нужно сделать, - это линейный поиск, начинающийся с конца очереди и заканчивающийся, когда вы либо находите дубликат (и отклоняете вставку), либо когда отметка времени превышает ваш лимит времени (и вставляете запись)? Кажется, намного проще, чем хранить другую структуру данных и всю связанную логику.

Я хотел бы создать класс-оболочку, а не расширять его из очереди, поскольку пользователи базового типа Queue ожидают другого поведения. (Контракты на данные в.NET 4.0 могут даже жаловаться, когда вы делаете это.)

Внутренне вы можете иметь реальную очередь для перенаправления необходимых звонков. При каждом вызове Queue() вы можете добавлять новый элемент в словарь, если он еще не содержится. Перед этим вы можете очистить все элементы, которые старше x секунд, из этого словаря и по порядку добавить их во внутреннюю очередь.

При снятии очереди вам придется проверить, содержит ли внутренняя очередь элементы, и в противном случае выбрать самый ранний элемент из словаря.

Это, конечно, только одна из возможных реализаций. Когда множество различных элементов могут быть поставлены в очередь быстро, словарь быстро заполняется, и для его решения может потребоваться дополнительная логика.

Другие вопросы по тегам