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. MySQL 5.5 服务器变量详解一(转)

    add by zhj:在MySQL5.6中对一些参数有增删改,详见http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html ...

  2. 【MySql】性能优化之分析命令

    一 当发现程序运行比较慢的时候,首先排除物力资源问题之后,就将注意力转向mysq数据库: 1.首先确定运行慢的sql语句: mysql> show full processlist; 2.确认低 ...

  3. 数据结构(C语言版)---第三章栈和队列 3.4.2 队列的链式表示和实现(循环队列)

    这个是循环队列的实现,至于串及数组这两章,等有空再看,下面将学习树. 源码如下: #include <stdio.h> #include <stdlib.h> #define ...

  4. HDU 2101 A + B Problem Too 分类: ACM 2015-06-16 23:57 18人阅读 评论(0) 收藏

    A + B Problem Too Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  5. 关于VBox安装GhostXP出现蓝屏processr.sys 的解决办法

    用VirtualBox虚拟系统安装了一个Ghost XP SP3,还原系统后,重启进入Windows XP时,出现蓝屏提示processr.sys, 蓝屏代码为0x000000CE提示处理器驱动文件问 ...

  6. ACM之数学题

    数学题,始终记得,第一次被带飞师大校赛以及省赛,毫无例外的在数学题上卡死....因此,现在开始,有意识的保留遇见的数学题...(下列知识点按遇见先后顺序排列: 1欧拉公式 欧拉公式的用处是,找出小于N ...

  7. 【Cocos2d-X开发学习笔记】第18期:动作类之改变动作对象、函数回调动作以及过程动作的使用

    本系列学习教程使用的是cocos2d-x-2.1.4(最新版为3.0alpha0-pre) ,PC开发环境Windows7,C++开发环境VS2010 一.改变动作执行对象 CCTargetedAct ...

  8. lght oj 1257 - Farthest Nodes in a Tree (II) (树dp)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1257 跟hdu2196一样,两次dfs //#pragma comment(l ...

  9. How Tomcat Works(三)

    上文中描述的简单的服务器是不符合Servlet规范的,所以本文进一步描述一个简单的Servlet容器是怎么实现的 所以我们首先要明白Servlet接口规范,规范有不同版本,本人就先一视同仁了: pub ...

  10. mysql max_allowed_packet错误

    mysql错误 max_allowed_packet 查看目前配置 show VARIABLES like '%max_allowed_packet%'; 修改: set global max_all ...