使用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
相关产品推荐
相关产品推荐

