基于IHostedService的磁盘监控Windows服务事件捕获失效问题排查
问题
我正在开发一款磁盘监控工具,目前使用简单的System.IO.FileSystemWatcher实现,但卡在了“持续运行”环节。
我的思路是:创建WatchService订阅FileSystemWatcher的所有事件(Created、Renamed、Deleted、Modified),事件触发时将文件名存入队列,每30秒左右批量发送至API——此举是因每个磁盘会触发大量FileSystemWatcher事件。
此前我已在WinForms应用中实现该功能,只要WinForms程序运行,FileSystemWatcher即可正常工作。但如今希望将其改造为Windows服务,故选择使用IHostedService。
目前遇到的问题:在StartAsync方法中创建WatchService实例后,若返回则服务停止,若用循环检测CancellationToken会重复创建实例。我的当前实现代码如下:
public class FileAgentService : IHostedService { private QueueService queueService; private bool isStarted = false; public FileAgentService(QueueService queueService) { this.queueService = queueService; } public Task StartAsync(CancellationToken cancellationToken) { var disks = DriveInfo.GetDrives().Where(x => x.IsReady == true); while (!cancellationToken.IsCancellationRequested) { if (!isStarted) { isStarted = true; foreach (var disk in disks) { WatchService service = new(disk.Name, queueService); } } } return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { throw new NotImplementedException(); } }
现在程序会为每个磁盘创建WatchService实例,但FileSystemWatcher的事件完全未被捕获,我认为实现存在问题。需求是Windows服务启动后为每个磁盘创建对应服务实例,持续运行并捕获事件,请问我哪里出错了?
解决方案
核心错误点
- WatchService实例被GC回收:你在StartAsync里创建的WatchService是局部变量,循环结束后没有全局引用持有这些实例,.NET垃圾回收器会很快回收它们,导致FileSystemWatcher被销毁,自然捕获不到事件。
- 阻塞StartAsync线程:用
while (!cancellationToken.IsCancellationRequested)循环会阻塞服务的启动线程,不符合IHostedService的设计规范——StartAsync应该快速完成初始化,而不是长期阻塞。 - 未实现资源释放:StopAsync没有清理FileSystemWatcher资源,服务停止时会导致资源泄漏,也可能影响下次启动。
修正后的代码实现
public class FileAgentService : IHostedService, IDisposable { private readonly QueueService _queueService; private readonly List<WatchService> _watchServices = new List<WatchService>(); public FileAgentService(QueueService queueService) { _queueService = queueService; } public Task StartAsync(CancellationToken cancellationToken) { // 获取所有就绪磁盘并创建对应的WatchService var disks = DriveInfo.GetDrives().Where(x => x.IsReady == true); foreach (var disk in disks) { var watchService = new WatchService(disk.Name, _queueService); _watchServices.Add(watchService); } // 直接返回完成任务,FileSystemWatcher基于事件监听,无需阻塞线程 return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { // 遍历所有WatchService,停止监控并释放资源 foreach (var service in _watchServices) { // 假设WatchService里有Stop/Dispose方法来关闭FileSystemWatcher service.Stop(); service.Dispose(); } _watchServices.Clear(); return Task.CompletedTask; } public void Dispose() { // 兜底释放资源,防止StopAsync未被调用时的泄漏 foreach (var service in _watchServices) { service.Dispose(); } } }
额外注意事项
- 确保你的
WatchService内部正确持有FileSystemWatcher实例,并且在构造函数中完成事件订阅和启动监控(比如设置EnableRaisingEvents = true)。 - 如果需要支持动态检测磁盘挂载/卸载,可以在后台启动一个定时任务(比如用
IHostedService的后台任务),定期扫描磁盘状态,新增磁盘时创建WatchService,移除时销毁对应实例。 - FileSystemWatcher本身有一些坑(比如重复事件、文件锁定问题),如果之前WinForms里已经处理过,迁移时要保留相关逻辑。
内容的提问来源于stack exchange,提问作者technologyresearcher
相关产品推荐
相关产品推荐

