服务中FileSystem Watcher未处理文件及服务启动Watcher实现咨询
看起来你在Windows服务里实现FileSystemWatcher+定时器处理文件的逻辑时遇到了文件未被处理的问题,我来给你梳理下核心问题点并提供完整的可运行方案:
核心问题分析
你当前的代码片段里缺少了几个关键环节:
- 没有使用线程安全的集合存储待处理文件:FileSystemWatcher的事件是在后台线程触发的,定时器的Elapsed事件也是另一个线程,普通List会引发并发访问异常,导致文件丢失。
- 没有正确订阅Watcher的文件事件(比如Created/Changed),也没开启事件触发开关。
- 没有处理文件刚创建时可能被占用的情况,直接处理会报错导致文件未被处理。
完整解决方案代码
下面是补全并优化后的代码,包含所有必要的逻辑:
using System; using System.Collections.Concurrent; using System.IO; using System.Threading; using System.Timers; using System.ServiceProcess; namespace FileWatcherService { public partial class FileProcessorService : ServiceBase { // 用线程安全的队列存储待处理文件,避免并发问题 private readonly ConcurrentQueue<string> _pendingFiles = new ConcurrentQueue<string>(); private FileSystemWatcher _watcher; private System.Timers.Timer _processingTimer; // 监控路径,可根据配置文件读取,这里示例硬编码 private readonly string _watchPath = @"C:\YourWatchDirectory"; public FileProcessorService() { InitializeComponent(); } protected override void OnStart(string[] args) { StartFileWatcher(); InitializeProcessingTimer(); } private void StartFileWatcher() { _watcher = new FileSystemWatcher { Path = _watchPath, // 监控文件名变化和文件写入完成事件 NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite, // 可根据需求修改过滤规则,比如只处理.txt文件:"*.txt" Filter = "*.*", // 如果需要监控子目录,设置为true IncludeSubdirectories = false }; // 订阅文件创建事件(文件写入完成后会触发) _watcher.Created += OnFileCreated; // 订阅Watcher错误事件,避免异常导致Watcher停止工作 _watcher.Error += OnWatcherError; // 开启事件触发 _watcher.EnableRaisingEvents = true; } private void InitializeProcessingTimer() { _processingTimer = new System.Timers.Timer(1000); // 1秒间隔 _processingTimer.Elapsed += ProcessPendingFiles; _processingTimer.AutoReset = true; // 每秒重复触发 _processingTimer.Enabled = true; } private void OnFileCreated(object sender, FileSystemEventArgs e) { // 将文件路径加入待处理队列 _pendingFiles.Enqueue(e.FullPath); } private void OnWatcherError(object sender, ErrorEventArgs e) { // 这里可以加入日志记录,比如记录Watcher错误信息 // Logger.Error($"文件监控器出错: {e.GetException().Message}"); // 可选:重启Watcher恢复监控 _watcher.EnableRaisingEvents = false; _watcher.Dispose(); StartFileWatcher(); } private void ProcessPendingFiles(object sender, ElapsedEventArgs e) { // 批量处理队列中的所有待处理文件 while (_pendingFiles.TryDequeue(out string filePath)) { try { // 执行文件处理逻辑 HandleFileProcessing(filePath); } catch (Exception ex) { // 记录处理异常,可根据需求决定是否重新加入队列(避免死循环) // Logger.Error($"处理文件 {filePath} 失败: {ex.Message}"); // 限制重试次数,比如最多重试2次 // if (retryCount < 2) // { // _pendingFiles.Enqueue(filePath); // } } } } private void HandleFileProcessing(string filePath) { if (!File.Exists(filePath)) return; bool isProcessed = false; int retryAttempts = 3; // 最多重试3次 // 处理文件可能被占用的情况,重试访问 while (!isProcessed && retryAttempts > 0) { try { // 以独占方式打开文件,确保文件已写入完成 using (var fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { // 这里替换成你的实际处理逻辑,比如读取文件内容、解析数据等 // var fileContent = new StreamReader(fileStream).ReadToEnd(); // ProcessContent(fileContent); // 处理完成后,将文件移动到归档目录 string archiveDir = Path.Combine(_watchPath, "ProcessedFiles"); Directory.CreateDirectory(archiveDir); string archivePath = Path.Combine(archiveDir, Path.GetFileName(filePath)); File.Move(filePath, archivePath); isProcessed = true; } } catch (IOException) { retryAttempts--; Thread.Sleep(500); // 等待500毫秒后重试 } } } protected override void OnStop() { // 停止定时器 _processingTimer?.Stop(); _processingTimer?.Dispose(); // 关闭文件监控器 _watcher?.EnableRaisingEvents = false; _watcher?.Dispose(); // 处理队列中剩余的文件,确保服务停止前完成所有待处理任务 while (_pendingFiles.TryDequeue(out string filePath)) { HandleFileProcessing(filePath); } } } }
关键注意事项
- 线程安全集合:必须使用
ConcurrentQueue(或ConcurrentBag)来存储待处理文件,避免多线程并发访问时的异常和数据丢失。 - 文件占用处理:文件刚创建时可能还在被写入进程占用,通过重试机制可以避免IOException,确保文件能被正常处理。
- Watcher错误恢复:订阅Error事件并实现重启逻辑,防止Watcher因意外异常停止监控。
- 服务停止清理:在OnStop方法中处理剩余文件,确保所有待处理任务都能完成后再停止服务。
内容的提问来源于stack exchange,提问作者techno
相关产品推荐
相关产品推荐

