直接用 File.Copy 或 File.ReadAllText 会失败,因文件被占用、磁盘不可用或网络抖动引发 IOException 等异常,而 .NET 原生 API 无重试机制;推荐用 Polly 实现带指数退避的健壮重试。

为什么直接用 File.Copy 或 File.ReadAllText 会失败?
文件被其他进程占用(如杀毒软件扫描、Excel 正在编辑)、磁盘临时不可用、网络共享路径抖动,都会导致 IOException、UnauthorizedAccessException 或 DirectoryNotFoundException。.NET 的原生文件 API 不带重试逻辑,抛出异常就终止,必须自己兜底。
用 RetryPolicy + Polly 是最稳的方案
硬写 for 循环 + Thread.Sleep 容易漏掉异常类型、超时控制或退避策略。推荐用 Polly 库(NuGet 安装 Polly 或 Polly.Extensions.Http)封装重试:
var retryPolicy = Policy
.Handle<IOException>()
.Or<UnauthorizedAccessException>()
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: attempt => TimeSpan.FromMilliseconds(Math.Pow(100, attempt)),
onRetry: (outcome, timespan, retryCount, context) =>
{
Console.WriteLine($"第 {retryCount} 次重试,等待 {timespan.TotalMilliseconds}ms");
});
<p>await retryPolicy.ExecuteAsync(() => File.WriteAllTextAsync("log.txt", "data"));
- 只捕获明确可重试的异常,
FileNotFoundException通常不该重试(路径根本不存在) -
Math.Pow(100, attempt)实现指数退避,避免瞬间重试加重资源争抢 - 注意异步方法要用
ExecuteAsync,同步操作用Execute
不用第三方库?手写重试要盯紧这三点
如果项目不能引入 Polly,手写也要保证健壮性:
- 必须限定重试异常类型,别用
catch (Exception)——OutOfMemoryException重试没意义 - 每次重试前加
Thread.Sleep或await Task.Delay,否则变成 CPU 空转 - 记录重试次数和最后一次异常,否则问题发生时无从排查 —— 比如写入日志:
Console.WriteLine($"写入失败,已重试 {retry} 次:{ex.Message}");
一个最小可用示例:
for (int i = 0; i < 3; i++)
{
try
{
File.Move("temp.txt", "final.txt");
break;
}
catch (IOException ex) when (i < 2)
{
await Task.Delay(TimeSpan.FromMilliseconds(100 * (int)Math.Pow(2, i)));
continue;
}
}
某些场景下重试反而有害
不是所有失败都适合重试:
-
PathTooLongException:路径超长是永久性错误,重试无效 - 权限不足(
UnauthorizedAccessException)且应用无提升权限能力时,反复重试只是延长失败感知时间 - 大文件复制中网络中断,重试可能从头开始,不如用断点续传(如
FileStream配合偏移量)
真正关键的是判断「失败是否大概率随时间自行恢复」—— 这个边界比代码逻辑更难把握。









