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

.NET 8中Windows服务运行时FileSystemWatcher失效问题

Windows服务中FileSystemWatcher停止工作的排查与解决

核心问题分析

代码在控制台运行正常,但转为Windows服务后FileSystemWatcher事件不触发,结合场景和代码,主要排查以下几个方向:

1. Async Void事件回调的陷阱

你使用async lambda处理Created事件,而FileSystemWatcher的事件回调基于同步线程池,async void会导致:

  • 异步操作脱离上下文,服务环境下线程池回收可能中断事件处理
  • 异步操作抛出异常时会直接终止进程,且无日志(服务环境下静默失败)

解决办法:
避免直接使用async void,改用同步包装异步操作并捕获异常:

_watcher.Created += (sender, e) =>
{
    _logger.LogInformation("File/directory created: {Path}", e.FullPath);
    // 用Task.Run包装异步方法,规避上下文问题
    _ = Task.Run(async () =>
    {
        try
        {
            await FileWatcherFileChanged(sender, e);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to handle created event for {Path}", e.FullPath);
        }
    });
};

2. FileSystemWatcher缓冲区溢出

默认InternalBufferSize仅8KB,监控目录文件变化频繁时,缓冲区易溢出导致事件丢失。控制台与服务环境的系统资源调度逻辑不同,更容易触发该问题。

解决办法:
增大缓冲区大小(最大支持64KB):

_watcher = new FileSystemWatcher
{
    Path = @"\\10.10.1.10\files$\1\2\3\",
    IncludeSubdirectories = true,
    NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName,
    InternalBufferSize = 65536 // 设置为64KB
};

3. 服务账户的网络共享访问限制

虽然验证了服务有文件访问权限,但Windows服务默认使用Local System账户时,访问网络共享存在限制:

  • Local System的网络身份是机器账户,可能无共享目录的持久访问权限
  • 服务启动时网络连接可能未完全建立,导致FileSystemWatcher无法初始化监控

解决办法:

  • 右键服务→属性→登录,选择域账户或本地管理员账户(确保该账户能直接访问共享路径)
  • 若必须使用Local System,需在共享目录权限中添加机器账户(DOMAIN\MACHINENAME$)的读写权限

4. 未处理FileSystemWatcher的错误事件

FileSystemWatcher遇到路径不可访问、权限变更等错误时,会静默停止触发事件。未监听Error事件无法排查这类问题。

解决办法:
添加错误事件处理,捕获异常日志:

_watcher.Error += (sender, e) =>
{
    _logger.LogError(e.GetException(), "FileSystemWatcher error for path {Path}", _watcher.Path);
};

5. 资源未正确释放(可选优化)

服务停止时,需禁用FileSystemWatcher事件并释放资源,避免内存泄漏或残留监控:

public override async Task StopAsync(CancellationToken cancellationToken)
{
    // 禁用事件并释放资源
    _watcher?.EnableRaisingEvents = false;
    _watcher?.Dispose();
    _watcher2?.EnableRaisingEvents = false;
    _watcher2?.Dispose();
    
    await base.StopAsync(cancellationToken);
}

修改后的完整代码示例

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly IManage _manage; // 假设_manage已通过依赖注入注入
    private FileSystemWatcher _watcher, _watcher2;

    public Worker(ILogger<Worker> logger, IManage manage)
    {
        _logger = logger;
        _manage = manage;
    }

    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        await base.StartAsync(cancellationToken);
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        _watcher?.EnableRaisingEvents = false;
        _watcher?.Dispose();
        _watcher2?.EnableRaisingEvents = false;
        _watcher2?.Dispose();
        
        await base.StopAsync(cancellationToken);
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        SetupFileSystemWatchers();
        return Task.CompletedTask;
    }

    private void SetupFileSystemWatchers()
    {
        try
        {
            _watcher = new FileSystemWatcher
            {
                Path = @"\\10.10.1.10\files$\1\2\3\",
                IncludeSubdirectories = true,
                NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName,
                InternalBufferSize = 65536
            };
            _watcher.Created += (sender, e) =>
            {
                _logger.LogInformation("Created: {FullPath}", e.FullPath);
                _ = Task.Run(async () =>
                {
                    try
                    {
                        await FileWatcherFileChanged(sender, e);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error handling created event for {FullPath}", e.FullPath);
                    }
                });
            };
            _watcher.Error += (sender, e) =>
            {
                _logger.LogError(e.GetException(), "Watcher error for path {Path}", _watcher.Path);
            };
            _watcher.EnableRaisingEvents = true;

            _watcher2 = new FileSystemWatcher
            {
                Path = @"\\10.10.1.10\files$\1\2\4\",
                IncludeSubdirectories = true,
                NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName,
                InternalBufferSize = 65536
            };
            _watcher2.Created += (sender, e) =>
            {
                _logger.LogInformation("Created: {FullPath}", e.FullPath);
                _ = Task.Run(async () =>
                {
                    try
                    {
                        await FileWatcherFileChanged(sender, e);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error handling created event for {FullPath}", e.FullPath);
                    }
                });
            };
            _watcher2.Error += (sender, e) =>
            {
                _logger.LogError(e.GetException(), "Watcher error for path {Path}", _watcher2.Path);
            };
            _watcher2.EnableRaisingEvents = true;

            _logger.LogInformation("Watchers set up successfully.");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error setting up file system watchers.");
        }
    }

    private async Task FileWatcherFileChanged(object source, FileSystemEventArgs e)
    {
        await _manage.RepoChanged(e.FullPath);
    }
}

内容的提问来源于stack exchange,提问作者user3704628

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 13:55:54