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. windows平台下,快速删除所有.svn文件夹

    新建一个注册表文件名为:DELSVN.reg编辑其内容如下: Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Cla ...

  2. datagrid url json

    <div class="easyui-accordion" style="width:500px;height:300px;"> <div t ...

  3. 【转】B树、B-树、B+树、B*树

    B树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B ...

  4. ACM 中常用的算法有哪些? 2014-08-21 21:15 40人阅读 评论(0) 收藏

    ACM 中常用的算法有哪些?作者: 张俊Michael 网络上流传的答案有很多,估计提问者也曾经去网上搜过.所以根据自己微薄的经验提点看法. 我ACM初期是训练编码能力,以水题为主(就是没有任何算法, ...

  5. datagridview里面有combox避免双击两次的写法

    双击两次变成单击一次的写法: void dataGridView_CellEnter(object sender, DataGridViewCellEventArgs e) { //实现单击一次显示下 ...

  6. android进度条的使用

    // 导入按钮事件  btnsearch.setOnClickListener(new View.OnClickListener() {      @Override   public void on ...

  7. Linux 调节屏幕亮度

    intel的核心显卡驱动是在 /sys/class/backlight/intel_backlight/ 目录下面的brightness文件中配置的. 可以通过查看max_brightness的值来确 ...

  8. 30.怎样在Swift中添加运行时属性?

    和OC一样,Swift中也可以添加运行时属性.下面将提供一个完整的例子,演示如何给按钮点击事件添加运行时属性. 1.示例 import UIKit var s_GofButtonTouchDownKe ...

  9. 【绝密外泄】风哥Oracle数据库DBA高级工程师培训视频教程与内部资料v0.1

    [绝密外泄]风哥Oracle数据库DBA高级工程师培训视频教程与内部资料v0.1 由于是[绝密外泄]资料,防止被查,需要的小伙伴赶紧下载附件中的课件文档.由于视频太大了,已放在百度网盘了,已经在附中说 ...

  10. PHP 获取js中变量的方法

    留作笔记 <php? $aaa="<script>document.write(1)</script>"; echo $aaa; ?> 其中数字 ...