C#文件锁定与解锁 C#如何处理文件并发访问冲突

来源:这里教程网 时间:2026-02-21 17:41:53 作者:

文件被占用时
IOException
怎么捕获和重试

直接对正在被其他进程或线程读写的文件调用

File.Open
File.WriteAllText
,大概率抛出
IOException
:“The process cannot access the file 'xxx' because it is being used by another process.”。这不是异常逻辑错误,而是典型并发竞争现象。

正确做法不是回避,而是主动处理:用

try/catch
捕获
IOException
,配合指数退避(exponential backoff)重试。注意不要无限制循环——建议最多 3–5 次,间隔从 10ms 开始逐次翻倍。

只捕获
IOException
,不捕获
UnauthorizedAccessException
等权限类异常,它们代表不同问题
重试前加
Thread.Sleep
或更推荐的
await Task.Delay
(异步上下文里)
重试逻辑别塞进业务主流程,抽成独立方法,比如
RetryOnFileLockAsync

FileStream
构造时如何设置
FileShare
参数

根本原因常在于打开方式太“霸道”。默认

File.OpenRead(path)
等价于
new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None)
—— 它禁止任何其他进程/线程同时访问该文件。

按实际协作需求调整

FileShare
是关键:

多个读者共存 → 用
</li>
<li>一个写者 + 多个读者 → 用 <code<FileShare.Read
(写入方必须独占
Write
,但允许别人只读)
完全不允许并发 → 保持
,但要确保持有时间最短</li>
</ul>
<p>示例:<pre class='brush:php;toolbar:false;'>using var fs = new FileStream(path, FileMode.Open, FileAccess.Write, FileShare.Read);
这样别的进程还能
OpenRead,但不能 <code>OpenWrite

为什么
File.Copy
File.Move
也报文件锁定

很多人以为复制/移动是“原子操作”,不会冲突——其实不然。

File.Copy
内部会先
Create
目标文件,再
Read
源文件流,全程可能被中断;
File.Move
在跨卷时本质是“复制+删除”,同样涉及多次文件访问。

解决思路一致:

对源文件,确认它没被其他程序以
FileShare.None
打开
对目标路径,确保没有同名文件正被写入(比如日志轮转中未关闭的
StreamWriter
跨进程场景下,优先考虑用命名互斥体(
Mutex
)协调,而不是靠重试硬扛

特别注意:Windows 中杀掉进程不一定立即释放句柄,尤其是 .NET 应用未显式

Dispose
FileStream
StreamWriter
时,GC 回收前文件锁一直挂着。

Mutex
实现跨进程文件访问协调

当重试和

FileShare
都不够用(比如多个独立进程需严格串行写同一配置文件),就得上同步原语。
Mutex
是 Windows 下最轻量、跨进程有效的选择。

关键点:

名称必须全局唯一,建议带产品名/路径哈希,如
"Global\MyApp_Config_Write_" + path.GetHashCode()
务必用
try/finally
确保
mutex.ReleaseMutex()
被执行,否则死锁
不要在
Mutex
持有期间做耗时操作(如网络请求、大文件读写),否则阻塞其他进程太久

示例节选:

var mutex = new Mutex(false, "Global\MyApp_Log_Write");<br>if (mutex.WaitOne(1000)) {<br>    try {<br>        File.AppendAllText(logPath, message);<br>    } finally {<br>        mutex.ReleaseMutex();<br>    }<br>} else {<br>    // 超时,说明别人正在写,可降级处理或抛自定义异常<br>}

真正难的不是加锁,而是判断哪些操作必须串行、哪些可以并行,以及锁粒度是否合理——锁整个文件还是只锁某段内容,差别很大。

相关推荐