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

使用FileSystemWatcher读取配置文件时遇文件占用异常求助

问题描述

我通过FileSystemWatcher实现了FileWatcher包装类,用来监听MySolution.main.config文件的修改,当文件变更时重新读取配置。但在OnChanged事件中执行以下代码时,抛出了文件被其他进程占用的异常:

var map = new ExeConfigurationFileMap { ExeConfigFilename = _appConfigFilePath };
var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

异常核心信息

System.Configuration.ConfigurationErrorsException
Inner Exception 1: IOException: The process cannot access the file 'C:\Source\Solutions\MySolution_1.2.3\MySolution\bin\Debug\MySolution.main.config' because it is being used by another process.

当前构造函数实现

public FileWatcher(string appConfigFilePath)
{
    _appConfigFilePath = appConfigFilePath;

    _lastRead = DateTime.MinValue;

    var watcher = new FileSystemWatcher(Path.GetDirectoryName(_appConfigFilePath))
    {
        Filter = Path.GetFileName(_appConfigFilePath),
        NotifyFilter = NotifyFilters.LastWrite,
        EnableRaisingEvents = true
    };

    watcher.Changed += OnChanged;
    watcher.Error += OnError;
}

尝试过简单锁机制但无效,求解决建议。


解决建议

1. 延迟读取+重试机制

文件修改后,编辑器或系统可能还持有文件句柄,立即读取会触发占用异常。可以在OnChanged中加入延迟,并添加重试逻辑,同时过滤重复触发的事件:

private readonly object _lockObj = new object();
private DateTime _lastProcessed = DateTime.MinValue;

private async void OnChanged(object sender, FileSystemEventArgs e)
{
    lock (_lockObj)
    {
        var currentTime = DateTime.Now;
        // 过滤1秒内的重复触发
        if (currentTime - _lastProcessed < TimeSpan.FromSeconds(1))
        {
            return;
        }
        _lastProcessed = currentTime;
    }

    // 先延迟500毫秒等待文件释放
    await Task.Delay(500);

    try
    {
        LoadConfig();
    }
    catch (IOException)
    {
        // 最多重试3次,每次间隔200毫秒
        for (int retry = 0; retry < 3; retry++)
        {
            await Task.Delay(200);
            try
            {
                LoadConfig();
                break;
            }
            catch
            {
                if (retry == 2) throw; // 最后一次重试失败再抛出异常
            }
        }
    }
}

private void LoadConfig()
{
    var map = new ExeConfigurationFileMap { ExeConfigFilename = _appConfigFilePath };
    var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
    // 这里处理读取到的配置
}

2. 手动读取文件内容再加载

绕过ConfigurationManager的默认文件读取逻辑,先以共享读模式读取文件内容到内存,再加载配置,避免句柄冲突:

private Configuration LoadConfigFromMemory()
{
    string configXml;
    // 以共享读写模式打开文件,避免独占
    using (var fs = new FileStream(_appConfigFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    using (var sr = new StreamReader(fs))
    {
        configXml = sr.ReadToEnd();
    }

    // 将XML内容加载到内存流
    using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(configXml)))
    {
        var map = new ExeConfigurationFileMap { ExeConfigFilename = _appConfigFilePath };
        var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
        config.Load(ms);
        return config;
    }
}

// 在OnChanged中调用
private void OnChanged(object sender, FileSystemEventArgs e)
{
    try
    {
        var config = LoadConfigFromMemory();
        // 处理配置
    }
    catch (Exception ex)
    {
        // 异常处理逻辑
    }
}

3. 临时禁用Watcher事件

FileSystemWatcher可能触发多次Changed事件,可以临时关闭事件触发,处理完成后再开启:

private void OnChanged(object sender, FileSystemEventArgs e)
{
    var watcher = sender as FileSystemWatcher;
    if (watcher == null) return;

    // 临时禁用事件,避免重复触发
    watcher.EnableRaisingEvents = false;

    try
    {
        // 延迟等待文件释放
        Thread.Sleep(500);
        var map = new ExeConfigurationFileMap { ExeConfigFilename = _appConfigFilePath };
        var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
        // 处理配置
    }
    finally
    {
        // 重新启用事件监听
        watcher.EnableRaisingEvents = true;
    }
}

4. 排查外部进程占用

如果以上方法无效,用Process Explorer这类工具查看文件句柄的持有者,确认是否有调试器、杀毒软件、文件同步工具等进程在占用配置文件,针对性排除干扰。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 22:01:03