快速启动简单的C#FileSystemWatcher Windows服务

时间:2014-05-21 19:34:53

标签: c# filesystemwatcher

我有一个用C#编写的Windows服务。在幕后它是一个FileSystemWatcher。 FSW会查找新文件并相应地处理它们。当我的服务启动时,它还需要处理现有文件。当我通过控制台应用程序执行此操作时,一切都按预期工作。

然而,当我尝试将这一切包装在Win服务中时,我的第一个问题是Win服务无法启动。它超时的原因是,即使最初要处理的文件很多,处理也需要很长时间。

以下是我“观察”课程的部分代码:

public WatcherService()
{
    _log.Debug("WatcherService instantiated.");
    _watcher = new FileSystemWatcher { Path = AppConfig.MonitorFolder, IncludeSubdirectories = true };

    // we want the watching to start BEFORE we process existing files
    // because if we do it the other way, a file might get missed
    _watcher.Created += File_OnChanged;
}

public void StartWatching()
{
    _log.Debug("WatcherService started.");
    // this kicks off the watching
    _watcher.EnableRaisingEvents = true; 

    // process existing files
    ProcessExistingFiles(AppConfig.MonitorFolder);
}

我的解决方法是启动FSW“观察”并在单独的异步线程上处理初始文件,就像这样(在我的Windows服务代码中):

protected override void OnStart(string[] args)
{
    _log.Debug("LoggingService starting.");

    // kick off the watcher on another thread so that the OnStart() returns faster; 
    // otherwise it will hang if there are a lot of files that need to be processed immediately
    Task.Factory.StartNew(() => _watcher.StartWatching()).ContinueWith(t =>
        {
            if (t.Status == TaskStatus.Faulted)
            {
                _log.Error("Logging service failed to start.", t.Exception.InnerException ?? t.Exception);
            }
        });
}

如果我没有在Task.Factory.StartNew()中包装那个“StartWatching”方法,那么OnStart()会超时,这是可以理解的。但现在看来我的StartWat()方法永远不会被调用。我在日志中看到“LoggingService starting”,但没有“WatcherService started”。 (编辑:仅供参考我也尝试过Task.Run(),但无济于事。)

怎么了?我确信我要么不明白StartNew()正在做什么和/或有更好的方法来做我想要完成的事情。

思想?

谢谢!

3 个答案:

答案 0 :(得分:3)

您可以完全避免线程化。只需在OnStart()方法中进行基本设置即可。部分设置是设置一个计时器在一两秒钟内完成。该计时器可以在当前线程上运行,但会在服务空闲之后发生。

这将解决问题,编写线程安全代码更容易。

答案 1 :(得分:0)

该服务必须设计为尽快从OnStart返回;可能在前30秒内。你只需要在里面做初始化动作。

您可以使用两种方法启动服务:
1.为长时间运行的操作创建一个线程
2.启动至少一个异步操作(timer,FileSystemWatching)

  1. 如果你有一个长时间运行的操作,你肯定要开始一个新的线程:

    public void OnStart(string[] args) 
    {
        var worker = new Thread(DoWork);
        worker.Name = "MyWorker";
        worker.IsBackground = false;
        worker.Start();
    }
    
    void DoWork()
    {
        // long running task
    }
    
  2. 在您的情况下,您的长时间运行操作正在监视文件更改。当您使用FileWatcher时,您可以在OnStart方法内启动文件监视,并使用线程池启动您的短期运行任务:

    protected override void OnStart(string[] args)
    {
        _log.Debug("LoggingService starting.");
    
        _log.Debug("Initializing FileSystemWatcher .");
        _watcher = new FileSystemWatcher { Path = AppConfig.MonitorFolder, IncludeSubdirectories = true };
        _watcher.Created += File_OnChanged;      
        _watcher.EnableRaisingEvents = true; 
        _log.Debug("FileSystemWatcher has been started.");
    
        ThreadPool.QueueUserWorkItem(ProcessExistingFiles, AppConfig.MonitorFolder);
        _log.Debug("Processing of existing files has been queued.");
    }
    
    private void ProcessExistingFiles(object folderPathObj)
    {
         var folderPath = folderPathObj as string;
         if (folderPath  != null)
         {
         }
    }
    

答案 2 :(得分:0)

我不了解Task.Factory.StartNew ...但我有一些类似的服务,我使用ThreadPool来做这类事。

另外,我不知道在单独的线程中运行StartWatching - 从你解释的内容我将把ProcessExistingFiles放在单独的线程中。例如:

public WatcherService()
{
    _log.Debug("WatcherService instantiated.");
    _watcher = new FileSystemWatcher { Path = AppConfig.MonitorFolder, IncludeSubdirectories = true };

    // we want the watching to start BEFORE we process existing files
    // because if we do it the other way, a file might get missed
    _watcher.Created += File_OnChanged;
}

public void StartWatching()
{
    _log.Debug("WatcherService started.");
    // this kicks off the watching
    _watcher.EnableRaisingEvents = true; 

    // process existing files

    // keep in mind that method should be
    // void ProcessExistingFiles(object param)
    // in order to satisfy Waitcallback delegate
    ThreadPool.QueueUserWorkItem(ProcessExistingFiles, AppConfig.MonitorFolder);

    // since you are now starting async - be sure not to process files multiple times
    // i.e. from File_OnChanged and ProcessExistingFiles
}