Страницы

Поиск по вопросам

суббота, 30 ноября 2019 г.

Как решить повтор события change в FileSystemWatcher?

#c_sharp #net


Мониторинг файла. При изменениях в файле почему-то дважды запускается Change. Как
можно это решить?

Вот код:

public void monitoring(string path)
{
    this.sPath = path;
    string first = Path.GetDirectoryName(path);
    string second = Path.GetFileName(path);
    FileSystemWatcher fsw = new FileSystemWatcher(first, second);
    fsw.Changed += new FileSystemEventHandler(watcher_Changed);
    fsw.EnableRaisingEvents = true;
}

public void watcher_Changed(object sender, FileSystemEventArgs e)
{
    MessageBox.Show("Be happy :3");
    SplitAndQuery(this.sPath);
}

    


Ответы

Ответ 1



Дело в том, что FileSystemWatcher опирается на уведомления от ОС. А запись изменений в файл далеко не всегда может являться атомарным действием: например, notepad.exe пишет на диск в несколько приемов, отдельно файл и отдельно его атрибуты, что повлечет за собой несколько срабатываний события Changed. Или при копировании/перемещении файлов запись происходит в несколько приемов. Избавиться от этого можно попробовать разными способами. Самый железобетонный, но не всегда подходящий -- отключать генерацию событий. Но пользоваться им надо аккуратно, т.к. можно пропустить другие полезные изменения. public void watcher_Changed(object sender, FileSystemEventArgs e) { watcher.EnableRaisingEvents = false; try { MessageBox.Show("Be happy :3"); SplitAndQuery(this.sPath); } finally { watcher.EnableRaisingEvents = true; } } Другой подход -- опираться на дату последней модификации файла (опционально еще и на e.ChangeType): DateTime lastWriteTime = DateTime.MinValue; public void watcher_Changed(object sender, FileSystemEventArgs e) { DateTime writeTime = File.GetLastWriteTime(this.sPath); if (lastWriteTime != writeTime) { MessageBox.Show("Be happy :3"); SplitAndQuery(this.sPath); lastWriteTime = writeTime; } } Если файл изменяется не очень часто, а события приходят одно за другим, то можно использовать таймер с некоторым ожиданием и отсекать события, для которых не прошло нужное количество времени. В общем, выбор решения целиков зависит от ваших требований (впрочем, как обычно :)).

Ответ 2



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

Ответ 3



Решение со счетчиком опасно, т.к. игнорирует возможные другие изменения. Есть вариант с отложенным исполнением, когда пропускаются изменения только в момент ожидания. private FileSystemWatcher _watcher = new FileSystemWatcher(); public void Start() { .... _watcher.Changed += _watcher_Changed; _watcher.EnableRaisingEvents = true; } private void _watcher_Changed(object sender, FileSystemEventArgs e) { new Thread(ReloadData).Start(); // запускаем в отдельном потоке, для отсечения очереди событий при многоэтапной записи в файл } private void ReloadData() { // блокируем выполнение для пропуска нескольких этапов записи файла if (Monitor.TryEnter(_watcher)) { try { Thread.Sleep(3000); // ждём 3 сек пока файл сохраняется } finally { Monitor.Exit(_watcher); } } else { return; // все остальные входы в течении 3 секунд будут игнорироваться } // через 3 секунды происходит реакция Execute(); }

Ответ 4



Нашел решение которое мне подходит, но не факт что подойдет всем: public void watcher_Changed(object sender, FileSystemEventArgs e) { changeCount++; if (changeCount == 1) { MessageBox.Show("Be happy :3"); SplitAndQuery(this.sPath); } else { changeCount = 0; MessageBox.Show("Be very happy, its works"); } }

Комментариев нет:

Отправить комментарий