1、问题描述
   程序里需要监视某个目录下的文件变化情况: 一旦目录中出现新文件或者旧的文件被覆盖,程序需要读取文件内容并进行处理。于是使用了下面的代码:

public void Initial()
{
System.IO.FileSystemWatcher fsw = new System.IO.FileSystemWatcher();
fsw.Filter = "*.*";
fsw.NotifyFilter = NotifyFilters.FileName |
NotifyFilters.LastWrite |
NotifyFilters.CreationTime; // Add event handlers.
fsw.Created += new FileSystemEventHandler(fsw_Changed);
fsw.Changed += new FileSystemEventHandler(fsw_Changed); // Begin watching.
fsw.EnableRaisingEvents = true;
} void fsw_Changed(object sender, FileSystemEventArgs e)
{
MessageBox.Show("Changed", e.Name);
}

  如果发现当一个文件产生变化时,Change事件被反复触发了好几次。这样可能的结果是造成同一文件的重复处理。

2、解决方案:
    通过一个计时器,在文件事件处理中让计时器延迟一段时间之后,再执行加载新的配置文件操作。这样可以避免对文件做一次操作触发了多个更改事件,而多次加载配置文件。

  研究了log4net的代码 - XmlConfigurator.cs,然后参照log4net对代码作了如下改动:
  基本思想是使用定时器,在事件触发时开始启动定时器,并记下文件名。当定时器到时,才真正对文件进行处理。
  (1)、定义变量

private int TimeoutMillis = ; //定时器触发间隔
System.IO.FileSystemWatcher fsw = new System.IO.FileSystemWatcher();
System.Threading.Timer m_timer = null;
List<String> files = new List<string>(); //记录待处理文件的队列

  (2)、初始化FileSystemWatcher和定时器

       fsw.Filter = "*.*";
fsw.NotifyFilter = NotifyFilters.FileName |
NotifyFilters.LastWrite |
NotifyFilters.CreationTime; // Add event handlers.
fsw.Created += new FileSystemEventHandler(fsw_Changed);
fsw.Changed += new FileSystemEventHandler(fsw_Changed); // Begin watching.
fsw.EnableRaisingEvents = true; // Create the timer that will be used to deliver events. Set as disabled
if (m_timer == null)
{
//设置定时器的回调函数。此时定时器未启动
m_timer = new System.Threading.Timer(new TimerCallback(OnWatchedFileChange),
null, Timeout.Infinite, Timeout.Infinite);
}

  (3)、文件监视事件触发代码:修改定时器,记录文件名待以后处理

        void fsw_Changed(object sender, FileSystemEventArgs e)
{
Mutex mutex = new Mutex(false, "FSW");
mutex.WaitOne();
if (!files.Contains(e.Name))
{
files.Add(e.Name);
}
mutex.ReleaseMutex(); //重新设置定时器的触发间隔,并且仅仅触发一次
m_timer.Change(TimeoutMillis, Timeout.Infinite);
}

  (4)、定时器事件触发代码:进行文件的实际处理

        private void OnWatchedFileChange(object state)
{
List<String> backup = new List<string>(); Mutex mutex = new Mutex(false, "FSW");
mutex.WaitOne();
backup.AddRange(files);
files.Clear();
mutex.ReleaseMutex(); foreach (string file in backup)
{
MessageBox.Show("File Change", file + " changed");
}
}

  将上面的代码整理一下,封装成一个类,使用上更加便利一些:

    public class WatcherTimer
{
private int TimeoutMillis = ; System.IO.FileSystemWatcher fsw = new System.IO.FileSystemWatcher();
System.Threading.Timer m_timer = null;
List<String> files = new List<string>();
FileSystemEventHandler fswHandler = null; public WatcherTimer(FileSystemEventHandler watchHandler)
{
m_timer = new System.Threading.Timer(new TimerCallback(OnTimer),
null, Timeout.Infinite, Timeout.Infinite);
fswHandler = watchHandler;
} public WatcherTimer(FileSystemEventHandler watchHandler, int timerInterval)
{
m_timer = new System.Threading.Timer(new TimerCallback(OnTimer),
null, Timeout.Infinite, Timeout.Infinite);
TimeoutMillis = timerInterval;
fswHandler = watchHandler;
} public void OnFileChanged(object sender, FileSystemEventArgs e)
{
Mutex mutex = new Mutex(false, "FSW");
mutex.WaitOne();
if (!files.Contains(e.Name))
{
files.Add(e.Name);
}
mutex.ReleaseMutex();
m_timer.Change(TimeoutMillis, Timeout.Infinite);
} private void OnTimer(object state)
{
List<String> backup = new List<string>();
Mutex mutex = new Mutex(false, "FSW");
mutex.WaitOne();
backup.AddRange(files);
files.Clear();
mutex.ReleaseMutex(); foreach (string file in backup)
{
fswHandler(this, new FileSystemEventArgs(
WatcherChangeTypes.Changed, string.Empty, file));
}
}
}

  在主调程序使用非常简单,只需要如下2步:
  1、生成用于文件监视的定时器对象

watcher = new WatcherTimer(fsw_Changed, TimeoutMillis);

  其中fsw_Changed是你自己的文件监视事件代码,将它传递给定时器对象的目的是用于定时到时的时候定时器对象可以调用你自己真正用于处理文件的代码。例如:

void fsw_Changed(object sender, FileSystemEventArgs e)
{
//Read file.
//Remove file from folder after reading }

  2、将FileSystemWatcher的Create/Change/Rename/Delete等事件句柄关联到定时器的事件上

fsw.Created += new FileSystemEventHandler(watcher.OnFileChanged);
fsw.Changed += new FileSystemEventHandler(watcher.OnFileChanged);
fsw.Renamed += new RenamedEventHandler(watcher.OnFileChanged);
fsw.Deleted += new FileSystemEventHandler(watcher.OnFileChanged);

  这一步的目的是当有任何文件监视事件发生时,都能通知到定时器,定时器可以从最后一次发生的事件开始计时,在该计时未到时之前的任何事件都只会重新使计时器计时,而不会真正触发文件监视事件。

  要注意的是,采用了以上的代码后,你真正用于处理文件监视事件的代码被调用的时候只有其中的e.Name是有值的。考虑到被监视的文件目录应该已经知道了,所以e.FullPath被赋值为string.Empty并不是不能接受的。

C# 之 FileSystemWatcher事件多次触发的解决方法的更多相关文章

  1. window.onresize 多次触发的解决方法

    用了window.onresize但是发现每次 onresize 后页面中状态总是不对,下面与大家分享下onresize 事件多次触发的解决方法. 之前做一个扩展,需要在改变窗口大小的时候保证页面显示 ...

  2. 关于 android 的setOnItemClickListener 和 setOnItemLongClickListener 同时触发的解决方法

    关于 android 的setOnItemClickListener 和 setOnItemLongClickListener 同时触发的解决方法. 其实方法也是很简单 的主要 setOnItemLo ...

  3. document.onclick在ios上不触发的解决方法与touchstart点击穿透处理

    document.onclick = function (e) { var e = e ? e : window.event; var tar = e.srcElement || e.target; ...

  4. vue使用svg,animate事件绑定无效问题及解决方法

    由于使用svg制作圆形进度条,但是进度展示的太生硬,没有过渡圆滑的效果,所以使用 animate(在svg元素里可以查到) 元素标签,但 这样使用了,还是没有效果,我前端使用的 vue ,所以通过 @ ...

  5. js中hover事件时候的BUG以及解决方法

    hover事件是我们在开发前段时候遇到的稀松平常的问题,但是有没有发现会出现有一个BUg,比如,你移动到一个元素上,让它执行一个方法,然后你快速的移入移出的时候,他会进行亮瞎你眼睛的频闪效果,而且跟得 ...

  6. ios微信浏览器click事件不起作用的解决方法

    $(document).on( "click", ".weui_cell", function (event) {alert(); }); JS代码是这样的,h ...

  7. WPF ComboBox SelectionChanged事件里赋值Text的解决方法

    string sCountry ; private void cbCountry_SelectionChanged(object sender, SelectionChangedEventArgs e ...

  8. jquery事件重复绑定的快速解决方法

    click等事件 解决:使用unbind("click")方法先解除绑定的事件再绑定新事件,即在给对象绑定事件之前先移除该对象上的原有事件 1 $("#test2&quo ...

  9. javascript 的button onclick事件不起作用的解决方法

    在项目中遇到个问题:servlet向前端返回如下按钮,当course_ID为数字是onclick事件正常,但当course_ID含有字母时onclick事件就不起作用.网上找了很多方法都不管用,最后自 ...

随机推荐

  1. rdlc Report Viewer Configuration Error

    主要是web.config没有配置 方法参考http://msdn.microsoft.com/zh-cn/library/ms251661.aspx

  2. [转]undefined reference问题总结

    转自http://ticktick.blog.51cto.com/823160/431329 最近在Linux下编程发现一个诡异的现象,就是在链接一个静态库的时候总是报错,类似下面这样的错误: (.t ...

  3. Oracle 10进制转36进制

    CREATE OR REPLACE FUNCTION IDFMS.func_dec236 (parm IN INT DEFAULT 0)   RETURN VARCHAR2IS   /*   10进制 ...

  4. socket 连接,使得地址马上可以重用

    /* 使地址马上可以重用 */                                                                                     ...

  5. 使用struts2实现文件上传

    action中 private File file;//文件 private String fileFileName;//文件名字 固定格式name+FileName private String f ...

  6. d3d导致cairo不正常

    最近要把cairo集成到项目中,却发现cairo不能工作了 折腾了两天才找到了原因:cairo的一个trick导致浮点数计算错误: http://blog.163.com/lvan100@yeah/b ...

  7. Left join 中On和Where的作用范围

    SQL语句如下: SELECT * FROM 表1 LEFT JOIN 表2 ON 表1.id = 表2.id AND 表2.Name != 'ff'WHERE 表1.NAME != 'aa' Lef ...

  8. Slave延迟很大的优化方法总结(MySQL优化)

    [http://www.cstor.cn/textdetail_9146.html] 一般而言,slave相对master延迟较大,其根本原因就是slave上的复制线程没办法真正做到并发.简单说,在m ...

  9. 一个简单的Java程序例子以及其几种注释

    在说道主题前,先来啰嗦两句,o()︿︶)o 唉,不说两句心里就有个疙瘩,也许这就是所谓的强迫症吧,好了说说我想啰嗦的,其实也就是这样子的,关于Java开发工具箱的下载以及环境的配置.Java开发工具箱 ...

  10. 转载:mysql update更新带子查询的实现方式

    出自:http://576017120.iteye.com/blog/1947154 mysql中更新时不能直接将更新的表作为查询的表,可以通过临时中间表的形式. 总结一下: 一:单表更新时 例如: ...