C# FileSystemWatcher跳过一些事件
如果你搜索C# FileSystemWatcher跳过一些事件,c#,filesystemwatcher,C#,Filesystemwatcher,如果你搜索FileSystemWatcher问题,你会发现很多关于FileSystemWatcher跳过一些事件(不是触发所有事件)的文章。基本上,如果您更改了监视文件夹中的许多文件,其中一些文件将不会由FileSystemWatcher处理 为什么会这样?我如何避免遗漏事件?原因 FileSystemWatcher正在监视某个文件夹中发生的更改。更改文件(例如创建文件)时,FileSystemWatcher会引发相应的事件。事件处理程序可以解压该文件,读取其内容以决定如何进一步处理它,在数据库
FileSystemWatcher
问题,你会发现很多关于FileSystemWatcher
跳过一些事件(不是触发所有事件)的文章。基本上,如果您更改了监视文件夹中的许多文件,其中一些文件将不会由FileSystemWatcher
处理
为什么会这样?我如何避免遗漏事件?原因
FileSystemWatcher
正在监视某个文件夹中发生的更改。更改文件(例如创建文件)时,FileSystemWatcher
会引发相应的事件。事件处理程序可以解压该文件,读取其内容以决定如何进一步处理它,在数据库日志表中写入该文件的记录,并将该文件移动到另一个文件夹。处理该文件可能需要一些时间
在此期间,可能会在关注的文件夹中创建另一个文件。由于FileSystemWatcher
的事件处理程序正在处理第一个文件,因此它无法处理第二个文件的创建事件。因此,FileSystemWatcher
遗漏了第二个文件。
解决方案
由于文件处理可能需要一些时间,并且其他文件的创建可能不会被FileSystemWatcher
检测到,因此文件处理应该与文件更改检测分开,并且文件更改检测应该非常短,以至于不会遗漏单个文件更改。文件处理可分为两个线程:一个用于文件更改检测,另一个用于文件处理。当文件被更改并且被FileSystemWatcher
检测到时,相应的事件处理程序应该只读取其路径,将其转发到文件处理线程并关闭自身,以便FileSystemWatcher
可以检测到另一个文件更改并使用相同的事件处理程序。处理线程可能需要处理文件所需的时间。队列用于将文件路径从事件处理程序线程转发到处理线程。
这是典型的生产者-消费者问题。有关生产者消费者队列的更多信息,请参见
代码
使用系统;
使用System.IO;
使用系统线程;
使用System.Collections.Generic;
命名空间文件系统监视示例{
班级计划{
静态void Main(字符串[]参数){
//如果未指定目录和筛选器,请退出程序
如果(参数长度!=2){
//显示调用程序的正确方式
WriteLine(“用法:Watcher.exe \“目录\”“过滤器\”);
返回;
}
FileProcessor FileProcessor=新的FileProcessor();
//创建新的FileSystemWatcher
FileSystemWatcher FileSystemWatcher 1=新建FileSystemWatcher();
//设置FileSystemWatcher的属性
fileSystemWatcher1.Path=args[0];
fileSystemWatcher1.Filter=args[1];
fileSystemWatcher1.IncludeSubdirectories=false;
//添加事件处理程序
fileSystemWatcher1.Created+=new System.IO.filesystemventhandler(此.fileSystemWatcher1_已创建);
//开始观看
fileSystemWatcher1.EnableRaisingEvents=true;
//等待用户退出程序
WriteLine(“按'q\'退出程序”);
while(Console.Read()!='q');
//关闭文件系统监视程序
if(fileSystemWatcher1!=null){
fileSystemWatcher1.EnableRaisingEvents=false;
fileSystemWatcher1.Dispose();
fileSystemWatcher1=null;
}
//处置文件处理器
if(文件处理器!=null)
Dispose();
}
//定义事件处理程序
已创建私有void fileSystemWatcher1_(对象发送方、FileSystemEventArgs e){
//如果文件已创建。。。
if(e.ChangeType==WatcherChangeTypes.Created){
//…将其文件名排入队列以便处理。。。
fileProcessor.EnqueueFileName(e.FullPath);
}
//…并立即完成事件处理程序
}
}
//文件处理器类
类文件处理器:IDisposable{
//创建一个AutoResteEventWaitHandle
private EventWaitHandle EventWaitHandle=new AutoResetEvent(false);
私人线程工作者;
私有只读对象锁定器=新对象();
专用队列fileNamesQueue=新队列();
公共文件处理器(){
//创建工作线程
工人=新线程(工作);
//启动工作线程
worker.Start();
}
public void排队文件名(字符串文件名){
//将文件名排队
//此语句由lock保护,以防止其他线程在对文件名排队时弄乱队列
lock(locker)fileNamesQueue.Enqueue(FileName);
//向worker发送信号,表明文件名已排队,并且可以对其进行处理
eventWaitHandle.Set();
}
私人工作(){
while(true){
字符串文件名=null;
//将文件名出列
锁(储物柜)
如果(fileNamesQueue.Count>0){
fileName=fileNamesQueue.Dequeue();
//如果文件名为空,则停止工作线程
如果(fileName==null)返回;
}
如果(文件名!=null){
//进程文件
进程文件(文件名);
}否则{
//没有更多文件名-等待信号
eventWaitHandle.WaitOne();
}
}
}
私有进程文件(字符串文件名){
//可能它必须等待创建该文件的进程停止使用该文件,然后才能继续
//解压文件
using System;
using System.IO;
using System.Threading;
using System.Collections.Generic;
namespace FileSystemWatcherExample {
class Program {
static void Main(string[] args) {
// If a directory and filter are not specified, exit program
if (args.Length !=2) {
// Display the proper way to call the program
Console.WriteLine("Usage: Watcher.exe \"directory\" \"filter\"");
return;
}
FileProcessor fileProcessor = new FileProcessor();
// Create a new FileSystemWatcher
FileSystemWatcher fileSystemWatcher1 = new FileSystemWatcher();
// Set FileSystemWatcher's properties
fileSystemWatcher1.Path = args[0];
fileSystemWatcher1.Filter = args[1];
fileSystemWatcher1.IncludeSubdirectories = false;
// Add event handlers
fileSystemWatcher1.Created += new System.IO.FileSystemEventHandler(this.fileSystemWatcher1_Created);
// Start to watch
fileSystemWatcher1.EnableRaisingEvents = true;
// Wait for the user to quit the program
Console.WriteLine("Press \'q\' to quit the program.");
while(Console.Read()!='q');
// Turn off FileSystemWatcher
if (fileSystemWatcher1 != null) {
fileSystemWatcher1.EnableRaisingEvents = false;
fileSystemWatcher1.Dispose();
fileSystemWatcher1 = null;
}
// Dispose fileProcessor
if (fileProcessor != null)
fileProcessor.Dispose();
}
// Define the event handler
private void fileSystemWatcher1_Created(object sender, FileSystemEventArgs e) {
// If file is created...
if (e.ChangeType == WatcherChangeTypes.Created) {
// ...enqueue it's file name so it can be processed...
fileProcessor.EnqueueFileName(e.FullPath);
}
// ...and immediately finish event handler
}
}
// File processor class
class FileProcessor : IDisposable {
// Create an AutoResetEvent EventWaitHandle
private EventWaitHandle eventWaitHandle = new AutoResetEvent(false);
private Thread worker;
private readonly object locker = new object();
private Queue<string> fileNamesQueue = new Queue<string>();
public FileProcessor() {
// Create worker thread
worker = new Thread(Work);
// Start worker thread
worker.Start();
}
public void EnqueueFileName(string FileName) {
// Enqueue the file name
// This statement is secured by lock to prevent other thread to mess with queue while enqueuing file name
lock (locker) fileNamesQueue.Enqueue(FileName);
// Signal worker that file name is enqueued and that it can be processed
eventWaitHandle.Set();
}
private void Work() {
while (true) {
string fileName = null;
// Dequeue the file name
lock (locker)
if (fileNamesQueue.Count > 0) {
fileName = fileNamesQueue.Dequeue();
// If file name is null then stop worker thread
if (fileName == null) return;
}
if (fileName != null) {
// Process file
ProcessFile(fileName);
} else {
// No more file names - wait for a signal
eventWaitHandle.WaitOne();
}
}
}
private ProcessFile(string FileName) {
// Maybe it has to wait for file to stop being used by process that created it before it can continue
// Unzip file
// Read its content
// Log file data to database
// Move file to archive folder
}
#region IDisposable Members
public void Dispose() {
// Signal the FileProcessor to exit
EnqueueFileName(null);
// Wait for the FileProcessor's thread to finish
worker.Join();
// Release any OS resources
eventWaitHandle.Close();
}
#endregion
}
}