C#文件操作中几乎不会发生死锁,但阻塞常见:主因是同步I/O阻塞、线程池耗尽或FileStream共享误用;典型表现为ReadAllBytes等长时间无响应,CPU低而线程数飙升。

死锁在 C# 文件操作中几乎不会发生,但阻塞很常见
文件 I/O 本身不涉及 .NET 的线程同步原语(如 lock、Monitor、Mutex),所以纯文件读写不会触发传统意义的“死锁”——即两个线程互相持有对方需要的锁。你遇到的“卡住”,大概率是同步 I/O 阻塞 + 线程池耗尽,或共享 FileStream 实例被多线程误用导致的无限等待。
典型现象:File.ReadAllBytes 或 new FileStream(...) 在高并发下长时间无响应,CPU 低,线程数飙升,调试器里看到大量线程停在 NativeOverlapped 或 WaitForSingleObject 上。
- Windows 下文件句柄是内核对象,
CreateFile失败(如权限不足、路径不存在)会直接抛异常,不会卡住;但打开后读写时若另一进程独占锁(如 Excel 正在编辑 .xlsx),FileStream构造函数就会阻塞(默认FileShare.None) - .NET 6+ 默认使用异步 I/O 路径,但如果你显式调用
Read/Write同步方法,仍会同步阻塞当前线程 - ASP.NET Core 中在
async方法里混用.Result或.Wait()是阻塞线程池线程的头号原因,比文件操作本身更易引发雪崩
FileStream 构造参数决定是否“卡住”,不是代码逻辑问题
关键不在你怎么写循环,而在你传给 FileStream 构造函数的 FileShare 和 FileAccess 是否匹配实际使用场景。比如多个线程同时以 FileAccess.ReadWrite 和 FileShare.None 打开同一文件,第二个线程就会永远等第一个关闭句柄。
-
FileShare.Read:允许多个读,但写入者必须独占 → 适合日志轮转场景(一个写,多个备份进程读) -
FileShare.ReadWrite:危险!仅适用于所有访问者都只做追加(FileOptions.Append)且不修改已有内容,否则数据损坏风险极高 -
FileAccess.Read+FileShare.Read:最安全的只读并发组合,但注意 Windows 下某些程序(如 Word)即使只读也会加共享锁,此时仍可能阻塞 - 务必显式指定
FileOptions.Asynchronous(.NET 5+ 默认开启),否则即使调用ReadAsync,底层也可能退化为同步 I/O
诊断阻塞点:别看线程名,要看 NativeHandle 和 WaitReason
在 Visual Studio 调试器中暂停后,打开“并行堆栈”窗口,右键任一线程 → “切换到线程”,然后在“线程”窗口中查看该线程的 WaitReason 字段。如果是 Executive 或 Synchronization,说明卡在内核等待;如果是 IOCompletionEx,说明在等 I/O 完成端口回调 —— 这反而是正常异步行为。
- 用 Process Explorer 查看目标进程的句柄列表,筛选
File类型,确认是否有大量重复路径句柄未释放(泄漏迹象) - 在代码中对关键
FileStream实例加using,并在Dispose前记录SafeFileHandle.IsInvalid和Handle值,避免静默失败后句柄没关 - 不要依赖
File.Exists判断后再打开 —— 检查和打开之间存在竞态窗口,直接尝试打开并捕获FileNotFoundException或UnauthorizedAccessException更可靠
真正要防的是共享资源误用,不是“死锁”
多数所谓“文件死锁”本质是:多个线程共用一个未加锁的 StreamWriter,或反复 File.AppendAllText 却没控制并发,导致底层 FileStream 被不同线程同时 Write → 内部缓冲区错乱、IOException 抛出,或更糟:静默覆盖。
- 避免全局静态
StreamWriter,改用ConcurrentQueue<action></action>+ 单独日志线程消费,或直接上Microsoft.Extensions.Logging -
File.AppendAllText内部每次都会新建FileStream(FileMode.Append,FileShare.Read),看似安全,但高频调用会触发 NTFS 元数据锁争用,性能骤降 - 真要并发写同一文件,唯一靠谱方案是:所有写入走同一个线程(生产者-消费者),或拆成多个分片文件(
log_001.txt,log_002.txt),由外部聚合
文件系统不是数据库,它没有事务、没有行锁、没有 MVCC。你以为的“并发安全”,往往只是还没撞上那个特定时间窗口。把文件当队列用,迟早出事。








