You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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服务启动后为每个磁盘创建对应服务实例,持续运行并捕获事件,请问我哪里出错了?

解决方案

核心错误点

  1. WatchService实例被GC回收:你在StartAsync里创建的WatchService是局部变量,循环结束后没有全局引用持有这些实例,.NET垃圾回收器会很快回收它们,导致FileSystemWatcher被销毁,自然捕获不到事件。
  2. 阻塞StartAsync线程:用while (!cancellationToken.IsCancellationRequested)循环会阻塞服务的启动线程,不符合IHostedService的设计规范——StartAsync应该快速完成初始化,而不是长期阻塞。
  3. 未实现资源释放: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 06:40:45