我有一个用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()正在做什么和/或有更好的方法来做我想要完成的事情。
思想?
谢谢!
答案 0 :(得分:3)
您可以完全避免线程化。只需在OnStart()
方法中进行基本设置即可。部分设置是设置一个计时器在一两秒钟内完成。该计时器可以在当前线程上运行,但会在服务空闲之后发生。
这将解决问题,编写线程安全代码更容易。
答案 1 :(得分:0)
该服务必须设计为尽快从OnStart返回;可能在前30秒内。你只需要在里面做初始化动作。
您可以使用两种方法启动服务:
1.为长时间运行的操作创建一个线程
2.启动至少一个异步操作(timer,FileSystemWatching)
如果你有一个长时间运行的操作,你肯定要开始一个新的线程:
public void OnStart(string[] args)
{
var worker = new Thread(DoWork);
worker.Name = "MyWorker";
worker.IsBackground = false;
worker.Start();
}
void DoWork()
{
// long running task
}
在您的情况下,您的长时间运行操作正在监视文件更改。当您使用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
}