慢速网络驱动器上,循环内using块的StreamWriter间歇性写入失败
问题分析与解决方案
核心原因
网络文件系统(如NAS、共享驱动器)的IO操作存在延迟,using块虽会自动释放StreamWriter,但底层FileStream的句柄可能因网络同步延迟,未被操作系统及时回收。当下一次循环尝试打开同一文件时,系统仍判定文件被占用,从而抛出IOException。
针对性解决方案
1. 显式刷新并强制关闭底层流
在using块内主动调用刷新和关闭方法,确保缓冲数据写入磁盘、文件句柄被及时释放:
private void FlushFifoToLogFile() { while (!_fifoLog.IsCompleted) { if (_fifoLog.Count > 0) { using (StreamWriter sw = new StreamWriter(_logFilePath, append:true)) { string line; while (_fifoLog.TryTake(out line)) { sw.WriteLine(line); } // 显式刷新缓冲并关闭底层流 sw.Flush(); sw.BaseStream.Close(); } } _flushCts.Token.WaitHandle.WaitOne(SLEEP_MILLISECONDS); } }
2. 自定义FileStream配置,禁用缓存
直接创建FileStream并指定共享模式与缓存选项,减少网络环境下的句柄残留:
private void FlushFifoToLogFile() { while (!_fifoLog.IsCompleted) { if (_fifoLog.Count > 0) { // 显式配置:允许后续进程(含当前进程)访问文件,强制数据直接写入磁盘 using (var fs = new FileStream(_logFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 4096, FileOptions.WriteThrough)) using (StreamWriter sw = new StreamWriter(fs)) { string line; while (_fifoLog.TryTake(out line)) { sw.WriteLine(line); } sw.Flush(); } } _flushCts.Token.WaitHandle.WaitOne(SLEEP_MILLISECONDS); } }
FileShare.ReadWrite允许后续打开操作访问文件,FileOptions.WriteThrough跳过系统缓存,缩短句柄持有时间。
3. 添加重试机制
针对网络延迟导致的偶发错误,加入重试逻辑,遇IOException时短暂等待后重试:
private void FlushFifoToLogFile() { const int maxRetries = 3; const int retryDelayMs = 500; while (!_fifoLog.IsCompleted) { if (_fifoLog.Count > 0) { int retryCount = 0; bool writeSuccess = false; while (retryCount < maxRetries && !writeSuccess) { try { using (StreamWriter sw = new StreamWriter(_logFilePath, append:true)) { string line; while (_fifoLog.TryTake(out line)) { sw.WriteLine(line); } } writeSuccess = true; } catch (IOException) { retryCount++; Thread.Sleep(retryDelayMs); } } } _flushCts.Token.WaitHandle.WaitOne(SLEEP_MILLISECONDS); } }
4. 优化写入批次逻辑
减少文件打开/关闭的频率,比如保持文件打开直到FIFO完成(业务允许的情况下):
private void FlushFifoToLogFile() { // 仅打开一次文件,避免重复打开时的句柄冲突 using (StreamWriter sw = new StreamWriter(_logFilePath, append:true)) { while (!_fifoLog.IsCompleted) { string line; while (_fifoLog.TryTake(out line)) { sw.WriteLine(line); } sw.Flush(); _flushCts.Token.WaitHandle.WaitOne(SLEEP_MILLISECONDS); } // 最后一次刷新确保所有数据写入 sw.Flush(); } }
额外验证点
- 确认日志文件路径生成逻辑无重复(比如时间戳精度不足导致多实例生成同一路径),可在生成路径后立即检查文件是否存在并打印日志,排除路径重复问题。
- 检查网络驱动器权限设置,确保进程拥有稳定的读写权限,避免权限波动引发的文件锁定。
内容的提问来源于stack exchange,提问作者amonroejj
相关产品推荐
相关产品推荐

