C# FileWatcher OnCreated事件快速连续写日志报文件被占用如何解决
问题核心说明
首先明确:写入前提前检测文件占用状态的方案没有生产可行性,这类方案属于典型的TOCTOU(检查时间/使用时间)竞态问题——你检测到文件未占用的瞬间,其他线程/进程完全可能立刻锁定文件,后续写入操作依然会抛出占用异常,无法从根源规避问题。
你当前代码有两个直接问题:
- 语法错误:
Path.Combine传入了3个参数,会把日志内容message拼接到文件路径中,且方法括号未闭合 - 并发缺陷:
File.WriteAllText默认以FileShare.Read模式打开文件,不支持多线程/多进程并发写入,且OnCreated事件由线程池触发,3个及以上文件同时创建时会出现多线程并发争抢文件锁的情况,直接抛出占用异常。
可行落地方案
方案1:进程内排队+带重试的共享写入(推荐,无第三方依赖)
核心思路是先通过信号量把同进程内的写入请求串行化,从根源避免同进程多线程抢锁;打开文件时指定允许共享读写的模式,搭配指数退避重试处理其他进程占用文件的极端场景,彻底规避锁异常。
// 进程内日志写入锁,保证同进程写入请求串行执行 private static readonly SemaphoreSlim _logWriteLock = new SemaphoreSlim(1, 1); public static async Task AppendLog(string message) { string logFullPath = Path.Combine(@"c:\logs", "mylog.txt"); const int maxRetryTimes = 5; int currentRetry = 0; int retryDelayMs = 100; // 等待进程内锁,避免同进程并发写入 await _logWriteLock.WaitAsync(); try { while (true) { try { // 以追加模式、共享读写模式打开文件,兼容跨进程并发写入场景 using var fileStream = new FileStream( path: logFullPath, mode: FileMode.Append, access: FileAccess.Write, share: FileShare.ReadWrite, bufferSize: 4096, useAsync: true); using var writer = new StreamWriter(fileStream); await writer.WriteLineAsync(message); // 写入成功直接跳出循环 break; } catch (IOException ex) when ((ex.HResult & 0x0000FFFF) is 32 or 33) // 32=文件被进程锁定,33=文件被其他进程占用,仅针对锁异常重试 { currentRetry++; if (currentRetry > maxRetryTimes) throw; // 超过最大重试次数直接抛出,避免无限等待 await Task.Delay(retryDelayMs); retryDelayMs *= 2; // 指数退避,降低重试撞锁概率 } } } finally { _logWriteLock.Release(); } }
调用时直接在OnCreated事件中用await AppendLog(你的日志内容)即可,注意要把OnCreated事件处理方法标记为async void(事件处理方法是唯一允许使用async void的场景)。
方案2:直接使用成熟日志库(生产环境优先选)
不需要自己实现并发、锁、重试逻辑,直接引入Serilog、NLog等成熟日志组件,这些组件内部已经实现了异步写入队列、文件锁竞争处理、滚动日志切割、异常兜底等能力,只需要简单配置即可实现稳定的文件日志写入,稳定性远高于自定义实现。
不推荐方案说明
不要实现类似下面的“文件占用检测”逻辑:
// 错误示例:该检测逻辑完全无法避免并发锁异常 public static bool IsFileLocked(string filePath) { try { using File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None); return false; } catch (IOException) { return true; } }
该逻辑执行完成到实际发起写入操作的时间差内,文件锁状态随时可能变化,检测结果没有参考价值,依然会随机出现锁占用异常。
另外补充注意点:FileSystemWatcher的OnCreated事件在部分系统场景下会对同一个新文件触发多次,建议增加简单的文件路径去重逻辑,避免重复写入日志。
内容的提问来源于stack exchange,提问作者levis84
相关产品推荐
相关产品推荐

