FileSystemWatcher跳过一些事件 [英] FileSystemWatcher skips some events

查看:59
本文介绍了FileSystemWatcher跳过一些事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果您用Google搜索 FileSystemWatcher 问题,则会发现很多有关 FileSystemWatcher 的文章,其中有些事件是跳过的(不是触发所有事件)。基本上,如果您更改了监视文件夹中的许多文件,其中的某些文件将不会被 FileSystemWatcher 处理。

If you google for FileSystemWatcher issues, you will find a lot of articles about FileSystemWatcher skipping some events (not firing all events). Basically, if you change a lot of files in watched folder some of them will not be processes by FileSystemWatcher.

为什么会这样,如何避免丢失事件?

Why is that so, and how can I avoid missing events?

推荐答案

原因



FileSystemWatcher 正在监视某些文件夹中发生的更改。更改文件(例如创建文件)后, FileSystemWatcher 会引发相应的事件。事件处理程序可能会解压缩文件,读取其内容以决定如何进一步处理,将其记录写入数据库日志表并将文件移至另一个文件夹。处理文件可能需要一些时间。

Cause

FileSystemWatcher is watching for changes happening in some folder. When file is changed (e.g. file is created), the FileSystemWatcher raises the appropriate event. The event handler might unzip the file, read its content to decide how to process it further, write record of it in database log table and move the file to another folder. The processing of the file might take some time.

在此期间,可能会在监视文件夹中创建另一个文件。由于 FileSystemWatcher 的事件处理程序正在处理第一个文件,因此它无法处理第二个文件的创建事件。因此, FileSystemWatcher 丢失了第二个文件。

During that time another file might be created in watched folder. Since FileSystemWatcher’s event handler is processing the first file, it cannot handle creation event of second file. So, the second file is missed by FileSystemWatcher.

由于文件处理可能花费一些时间, FileSystemWatcher 可能无法检测到其他文件的创建,文件处理应与文件更改检测分开,并且文件更改检测应如此之短,以至于不会丢失单个文件更改。文件处理可以分为两个线程:一个用于文件更改检测,另一个用于文件处理。当文件被更改并且被 FileSystemWatcher 检测到时,适当的事件处理程序应仅读取其路径,将其转发到文件处理线程并关闭自身,因此 FileSystemWatcher 可以检测到另一个文件更改并使用相同的事件处理程序。处理线程可能需要处理文件所需的时间。队列用于将文件路径从事件处理程序线程转发到处理线程。

Since file processing might take some time and creation of other files might get undetected by FileSystemWatcher, file processing should be separated from file change detection and file change detection should be so short that it never misses single file change. File handling can be divided into two threads: one for the file change detection and the other for the file processing. When file is changed and it is detected by FileSystemWatcher, appropriate event handler should only read its path, forward it to file processing thread and close itself so FileSystemWatcher can detect another file change and use the same event handler. The processing thread could take as much time as it needs to process the file. A queue is used for forwarding file path from event handler thread to the processing thread.

这是典型的生产者-消费者问题。有关生产者-消费者队列的更多信息,请参见此处

This is classic producer-consumer problem. More about producer-consumer queue can be found here.

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
    }
}

这篇关于FileSystemWatcher跳过一些事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆