【发布时间】:2014-10-11 13:22:11
【问题描述】:
我现在收到 3 份报告称用户的机器在使用我的软件时崩溃了。这些崩溃与我的程序无关,但是当他们重新启动配置文件时,我的程序写入的配置文件都已损坏。
文件的写入方式没有什么特别之处,只需创建一个 Json 表示并使用 File.WriteAllText() 将其转储到磁盘
// save our contents to the disk
string json = JsonConvert.SerializeObject(objectInfo, Formatting.Indented);
// write the contents
File.WriteAllText(path, json);
我有一个用户给我发了一个文件,长度看起来差不多(~3kb),但内容都是 0x00。
根据下面的帖子 File.WriteAllText 应该关闭文件句柄,将所有未写入的内容刷新到磁盘:
In my C# code does the computer wait until output is complete before moving on?
但是,正如 Alberto 在 cmets 中指出的那样:
System.IO.File.WriteAllText 完成后,会将所有文本刷新到 文件系统缓存,然后,它将被延迟写入驱动器。
所以我推测这里发生的事情是文件正在被清除并使用 0x00 初始化,但系统崩溃时尚未写入数据。
我在考虑可能使用某种临时文件,所以过程会是这样的:
- 将新内容写入临时文件
- 删除原始文件
- 将临时文件重命名为原始文件
我认为这不会解决问题,因为我认为 Windows 只会移动文件,即使 IO 仍处于挂起状态。
有什么办法可以强制机器将该数据转储到磁盘,而不是由它决定何时执行此操作,或者可能是更新文件的更好方法?
更新:
根据@usr、@mikez 和@llya luzyanin 的建议,我创建了一个新的WriteAllText 函数,它使用以下逻辑执行写入:
- 使用 FileOptions.WriteThrough 标志创建一个包含新内容的临时文件
- 将数据写入磁盘(在写入完成之前不会返回)
- File.Replace 将新临时文件的内容复制到真实文件,进行备份
按照这种逻辑,如果最终文件无法加载,我的代码会检查备份文件并改为加载该文件
代码如下:
public static void WriteAllTextWithBackup(string path, string contents)
{
// generate a temp filename
var tempPath = Path.GetTempFileName();
// create the backup name
var backup = path + ".backup";
// delete any existing backups
if (File.Exists(backup))
File.Delete(backup);
// get the bytes
var data = Encoding.UTF8.GetBytes(contents);
// write the data to a temp file
using (var tempFile = File.Create(tempPath, 4096, FileOptions.WriteThrough))
tempFile.Write(data, 0, data.Length);
// replace the contents
File.Replace(tempPath, path, backup);
}
【问题讨论】:
-
老实说,我认为您最好花时间在这种情况下,确定导致系统崩溃的原因。
-
它们只是人们机器上的随机崩溃,与我的系统无关(我认为其中一个被报告为驱动程序问题)。无论如何,重点是,作为软件开发人员,我的用户要求我确保我的程序在收到 BSOD 或其他情况时不会丢失其配置
-
您是否尝试过使用
File.Create方法而不是File.WriteAllText?它有有用的重载——msdn.microsoft.com/en-us/library/ms143360(v=vs.110).aspx,它带有 FileOptions 参数,可以设置为WriteThrough——“指示系统应该通过任何中间缓存写入并直接进入磁盘。” -
您可能会考虑手动创建
FileStream并使用FileOptions.WriteThrough选项。 This 问题展示了如何禁用所有缓冲,这只能通过 PInvoke 完成。 -
@miked 如果您使用 WriteThrough 写入 1GB 我认为相信可以部分写入数据是直观的。当调用返回时数据是稳定的,但在此之前您可以混合新旧数据。