Надежное решение для событий запуска FileSystemWatcher несколько раз

FileSystemWatcher события могут срабатывать несколько раз. Не хорошо, если мне нужно предсказуемое поведение из моего кода.

Это описано в документации MSDN:

Обычные операции с файловой системой могут вызывать более одного события. Например, когда файл перемещается из одного каталога в другой, могут возникнуть несколько событий OnChanged и некоторые события OnCreated и OnDeleted. Перемещение файла - это сложная операция, которая состоит из нескольких простых операций, поэтому вызывает несколько событий. Аналогично, некоторые приложения (например, антивирусное программное обеспечение) могут вызывать дополнительные события файловой системы, которые обнаруживаются FileSystemWatcher.

Хорошее использование NotifyFilters с конкретными событиями помогло, но не даст мне 100% уверенности в последовательности.

Вот пример, воссоздающий пример записи Блокнота (но я испытал это и с другими действиями записи):

 public ExampleAttributesChangedFiringTwice(string demoFolderPath)
 {
     var watcher = new FileSystemWatcher()
     {
          Path = @"c:\temp",
          NotifyFilter = NotifyFilters.LastWrite,
          Filter = "*.txt"
      };

      watcher.Changed += OnChanged;
      watcher.EnableRaisingEvents = true;
  }

  private static void OnChanged(object source, FileSystemEventArgs e)
  {
      // This will fire twice if I edit a file in Notepad
  }

Любые предложения по повышению устойчивости?

РЕДАКТИРОВАТЬ: означает не повторять несколько действий при запуске нескольких событий.

2 ответа

Решение

Подход, использующий MemoryCache в качестве буфера, который будет "душить" дополнительные события.

  1. Файловое событие (Changed в этом примере) срабатывает
  2. Событие обрабатывается OnChanged но вместо завершения требуемого действия, оно сохраняет событие в MemoryCache с истечением 1 секунды и CacheItemPolicy Настройка обратного вызова для выполнения по истечении срока.

Обратите внимание, что я использую AddOrGetExisting как простой способ блокировать любые дополнительные события в течение периода кеша, добавляемого в кеш.

  1. Когда он истекает, обратный вызов OnRemovedFromCache завершает поведение, предназначенное для этого события файла

,

  class BlockAndDelayExample
{
    private readonly MemoryCache _memCache;
    private readonly CacheItemPolicy _cacheItemPolicy;
    private const int CacheTimeMilliseconds = 1000;

    public BlockAndDelayExample(string demoFolderPath)
    {
        _memCache = MemoryCache.Default;

        var watcher = new FileSystemWatcher()
        {
            Path = demoFolderPath,
            NotifyFilter = NotifyFilters.LastWrite,
            Filter = "*.txt"
        };

        _cacheItemPolicy = new CacheItemPolicy()
        {
            RemovedCallback = OnRemovedFromCache
        };

        watcher.Changed += OnChanged;
        watcher.EnableRaisingEvents = true;
    }

    // Add file event to cache for CacheTimeMilliseconds
    private void OnChanged(object source, FileSystemEventArgs e)
    {
        _cacheItemPolicy.AbsoluteExpiration =
            DateTimeOffset.Now.AddMilliseconds(CacheTimeMilliseconds);

        // Only add if it is not there already (swallow others)
        _memCache.AddOrGetExisting(e.Name, e, _cacheItemPolicy);
    }

    // Handle cache item expiring
    private void OnRemovedFromCache(CacheEntryRemovedArguments args)
    {
        if (args.RemovedReason != CacheEntryRemovedReason.Expired) return;

        // Now actually handle file event
        var e = (FileSystemEventArgs) args.CacheItem.Value;
    }
}

Может легко распространяться на:

  • Проверьте блокировку файла по истечении срока действия кеша и, если он недоступен, снова поместите его в кеш (иногда события запускаются так быстро, что файл не готов к некоторым операциям). Предпочтительнее использовать циклы try/catch.
  • Кеш ключей на имя файла + тип события в сочетании

Я использую FileSystemWatcher для проверки загружаемых файлов MP4, с которыми мне в конечном итоге придется что-то делать. Процесс, выполняющий загрузку, похоже, не устанавливает какой-либо блокировки на файл, поэтому раньше я пытался начать их обработку слишком рано.

Методика, которую я принял в конце, которая была полностью успешной для моего случая, состояла в том, чтобы использовать событие и добавить путь к файлу в Dictionary<string, long> потенциально интересных файлов и запустить таймер. Периодически (60 секунд) я проверяю размер файла. long Значение словаря содержит размер файла из последней проверки, и если текущий размер больше, я считаю, что он все еще записывается, сохраните новый размер и вернитесь в спящий режим еще на 60 секунд.

После 60 секунд, в течение которых запись не выполнялась, я могу начать обработку.

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

В конечном счете, учтите, что FileSYstemWatcher может быть полезным устройством не для уведомления вас о том, с какими файлами вы должны работать, а с файлами, которые могут быть вам интересны, и отдельный процесс с более утонченной внутренней логикой может решить, должен ли потенциально интересный файл действовать на

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