在C#开发中,如果程序在写文件时突然崩溃,很可能只写了一半数据,导致文件损坏。要解决这个问题,可以设计一个带预写日志和原子替换机制的可恢复写入操作,保证要么写入完整生效,要么崩溃后能通过日志恢复或回滚。

核心设计思路
可恢复写入一般包含三个关键步骤:先写临时数据文件与日志,再原子提交,最后清理旧文件。崩溃后重启时,根据日志判断上一次操作是否完成。
- 使用临时文件写入新内容,不影响原文件
- 用日志文件记录提交状态
- 通过重命名实现原子替换
- 启动时检查日志并恢复
基础实现示例
下面代码展示了一个简单的可恢复写入器,利用临时文件和日志完成安全写入。
using System;
using System.IO;
public class RecoverableWriter
{
private string _path;
private string _tempPath;
private string _logPath;
public RecoverableWriter(string path)
{
_path = path;
_tempPath = path + ".tmp";
_logPath = path + ".log";
}
// 崩溃恢复,在程序启动时调用
public void Recover()
{
if (File.Exists(_logPath))
{
string state = File.ReadAllText(_logPath);
if (state == "committed" && File.Exists(_tempPath))
{
// 提交未完成,补做替换
if (File.Exists(_path)) File.Delete(_path);
File.Move(_tempPath, _path);
}
else
{
// 未提交,清理临时文件
if (File.Exists(_tempPath)) File.Delete(_tempPath);
}
File.Delete(_logPath);
}
else if (File.Exists(_tempPath))
{
File.Delete(_tempPath);
}
}
// 写入数据
public void WriteAllText(string content)
{
File.WriteAllText(_tempPath, content);
// 写日志:准备提交
File.WriteAllText(_logPath, "ready");
// 原子替换
if (File.Exists(_path)) File.Delete(_path);
File.Move(_tempPath, _path);
// 写日志:已提交
File.WriteAllText(_logPath, "committed");
File.Delete(_logPath);
}
}
使用方式
在程序初始化时先调用恢复方法,再正常使用写入功能。
var writer = new RecoverableWriter("data.txt");
writer.Recover();
writer.WriteAllText("hello recoverable");
注意事项
上述方案依赖文件系统的重命名原子性,在本地磁盘上通常可靠。如果写入量很大,建议改为分块写日志并在日志中记录偏移与校验和,启动后按块校验恢复。
设计可恢复写入的重点是:临时隔离写入、明确提交边界、崩溃后幂等恢复。
小结
通过临时文件、状态日志和原子替换,C#可以实现一个在崩溃后仍能恢复的文件写入操作,避免数据文件处于中间态。实际项目中可结合业务增加校验和或事务批次,进一步提高健壮性。