Надежное решение для событий запуска 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 в качестве буфера, который будет "душить" дополнительные события.
- Файловое событие (
Changed
в этом примере) срабатывает - Событие обрабатывается
OnChanged
но вместо завершения требуемого действия, оно сохраняет событие вMemoryCache
с истечением 1 секунды иCacheItemPolicy
Настройка обратного вызова для выполнения по истечении срока.
Обратите внимание, что я использую AddOrGetExisting
как простой способ блокировать любые дополнительные события в течение периода кеша, добавляемого в кеш.
- Когда он истекает, обратный вызов
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 может быть полезным устройством не для уведомления вас о том, с какими файлами вы должны работать, а с файлами, которые могут быть вам интересны, и отдельный процесс с более утонченной внутренней логикой может решить, должен ли потенциально интересный файл действовать на