fileshare.none 是 filestream 默认值,导致其他进程或线程无法同时访问文件;应按需指定 fileshare.read、write 或 readwrite,并配合 lock、原子替换或同步机制实现线程安全。

FileShare.None 会导致“文件正由另一进程使用”的典型错误
当用 FileStream 打开文件却没指定 FileShare,默认是 FileShare.None——意味着其他任何进程(甚至本进程的另一个线程)都无法同时读或写该文件。常见于日志写入、配置文件热加载等场景:一个线程在写日志,另一个线程尝试读取日志归档,立刻抛出 IOException: The process cannot access the file...。
解决思路不是“避免并发”,而是明确声明你允许什么操作共存:
-
FileShare.Read:允许多个读取者同时打开(适合只读配置文件被多个模块加载) -
FileShare.Write:允许多个写入者(极少单独用,通常搭配Read) -
FileShare.ReadWrite:最宽松,但需自行保证写逻辑线程安全(如日志轮转时多个线程写同一文件) - 注意:
FileShare控制的是“打开权限”,不解决写入冲突;它只是让Open调用不失败
日志写入场景下 FileShare.ReadWrite + FileStream.Lock 的组合更可靠
单纯设 FileShare.ReadWrite 并不能防止多线程写乱数据。比如两个线程同时调用 stream.Write(),可能产生交错字节。这时需要配合 FileStream.Lock() 和 Unlock() 显式加锁:
using var fs = new FileStream("app.log", FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
fs.Lock(0, fs.Length); // 锁定整个文件(或指定偏移+长度)
try
{
fs.Write(Encoding.UTF8.GetBytes("log entry\n"));
fs.Flush();
}
finally
{
fs.Unlock(0, fs.Length);
}
关键点:
-
Lock()是操作系统级 advisory lock,仅对同样调用Lock()的代码生效;不加锁的读写仍能进行(但可能读到中间状态) - 若要强一致性,建议改用
TextWriter.Synchronized()包装,或直接用System.IO.Pipelines配合单生产者/多消费者队列 -
FileShare.ReadWrite是前提——没有它,第二个线程连FileStream都构造不出来
配置文件热重载必须用 FileShare.Read,且需处理读写竞争
程序启动后持续监听 appsettings.json 变化并重新加载,此时主程序在读,文件系统监视器(FileSystemWatcher)触发后可能立即写入新内容。若读取时用了 FileShare.None,写入会失败;若写入时没设 FileShare.Read,读取会失败。
推荐做法:
- 读配置:用
File.OpenRead(path)(隐含FileShare.Read),或显式new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read) - 写配置(如保存用户设置):用
FileMode.Create+FileShare.Read,确保写入时允许其他线程读旧版本 - 避免直接覆盖原文件;先写临时文件,再
File.Replace()原子替换,减少读写窗口期
FileShare.Inheritable 在跨进程通信中容易被忽略
当用 Process.Start() 启动子进程,并希望子进程继承父进程已打开的文件句柄时,必须在创建 FileStream 时传入 FileShare.Inheritable。否则子进程调用 GetStdHandle() 或通过句柄传递机制拿到的句柄无效。
典型误用:
- 忘记在
FileStream构造参数里叠加该 flag:FileShare.Read | FileShare.Inheritable - 在 .NET Core / .NET 5+ 中,Windows 上默认禁用句柄继承,需额外设置
ProcessStartInfo.UseShellExecute = false且ProcessStartInfo.RedirectStandardXXX = true才生效 -
FileShare.Inheritable仅影响句柄是否可继承,不影响文件访问权限本身
FileShare 当成“并发控制方案”来用——它只管“能不能打开”,不管“打开后怎么读写”。真正的协调还得靠锁、队列、原子替换或内存映射。










