FileStream 本身不保证读写顺序
很多人以为只要用 FileShare.ReadWrite 就能“公平”处理并发访问,其实这是误解。.NET 的 FileStream 底层依赖操作系统文件句柄,而 Windows/Linux 对文件共享的调度是无序的——先抢到句柄的线程/进程就上,不管它是要读还是写。所谓“公平”,必须靠上层逻辑控制。
FileShare 只决定能否同时打开,不干预后续 I/O 的执行时机或优先级new FileStream(...) 时,构造函数返回不代表已获得磁盘访问权,真正阻塞可能发生在第一次 Read() 或 Write()ReaderWriterLockSlim 做内存协调,但注意它不锁文件本身
真正的公平读写锁得自己组装:用 ReaderWriterLockSlim 管理“谁可以发起文件操作”,再配合实际的 FileStream 操作。但它只锁代码路径,不锁磁盘,所以必须确保所有文件访问都经过同一把锁实例,且不能绕过。
ReaderWriterLockSlim 实例,不能每次操作都 new 一个EnterReadLock() + TryEnterReadLock() 配合超时,避免写线程长期被饿死EnterWriteLock(),且内部要尽快完成 —— 锁住期间其他读写全卡住finally 块里调用 ExitReadLock() 或 ExitWriteLock(),否则锁泄漏会导致整个系统僵死|
|
多数场景下,与其费力模拟公平,不如换思路:让写操作走独占、原子、低延迟路径,读操作尽量缓存或降级。例如日志文件、配置快照这类典型用例,写入用 FileMode.Create + FileAccess.Write + FileShare.None,读取则从内存缓存或副本拿。
FileMode.Append 虽然允许并发打开,但多线程写入仍可能交错(Windows 下尤其明显),不是线程安全的追加FileStream.Lock() 手动锁住文件区域,但跨进程无效,且容易死锁MemoryMappedFile)+ 自定义读写计数器,比纯 FileStream 更可控ReaderWriterLockSlim,得用命名同步原语
ReaderWriterLockSlim 是进程内锁,跨进程时完全失效。如果你的程序有多个实例(比如 Windows 服务 + GUI 工具同时操作同一文件),必须升级到系统级同步机制。
Mutex 或 Semaphore 配合命名前缀(如 "Global\MyApp_FileAccess"),注意权限问题,普通用户可能无法访问 Global 命名空间EventWaitHandle 可以做读写信号量,但需要你自己维护读计数器和写状态,容易出错NamedPipeServerStream),把文件操作收口到一个守护进程里,其他进程只发请求
公平从来不是文件系统的默认属性,而是你对访问路径、资源粒度和错误容忍度权衡后的结果。最容易被忽略的一点:当多个线程都在等同一个文件锁时,它们的等待顺序取决于 OS 调度器,而不是你代码里的 EnterReadLock() 调用顺序。
版权声明: 本站资源均来自互联网或会员发布,如果侵犯了您的权益请与我们联系,我们将在24小时内删除!谢谢!联系QQ:76900276