私のソフトウェアを使用しているときにユーザーのマシンがクラッシュしたという報告が 3 件ありました。クラッシュは私のプログラムとは関係ありませんが、構成ファイルを再起動すると、プログラムが書き込んだ内容がすべて破損します。
ファイルの書き込み方法には特別なことはなく、JSON表現を作成し、File.WriteAllText()を使用してディスクにダンプするだけです。
// save our contents to the disk
string json = JsonConvert.SerializeObject(objectInfo, Formatting.Indented);
// write the contents
File.WriteAllText(path, json);
あるユーザーからファイルの 1 つが送られてきましたが、長さはほぼ適切 (約 3kb) に見えますが、内容はすべて 0x00 です。
以下の投稿によると、File.WriteAllText はファイル ハンドルを閉じて、書き込まれていないコンテンツをディスクにフラッシュする必要があります。
私の C# コードでは、コンピューターは出力が完了するまで待機してから先に進みますか?
しかし、Alberto がコメントで指摘したように:
System.IO.File.WriteAllText が完了すると、すべてのテキストがファイルシステム キャッシュにフラッシュされ、その後、ドライブに遅延書き込みされます。
したがって、ここで起こっていることは、ファイルがクリアされ、0x00 で初期化されているが、システムがクラッシュしたときにデータはまだ書き込まれていないということだと推測します。
何らかの一時ファイルを使用することを考えていたので、プロセスは次のようになります。
- 新しい内容を一時ファイルに書き込む
- 元のファイルを削除する
- 一時ファイルの名前をオリジナルに変更
IO がまだ保留中であっても Windows はファイルを移動するだけだと思うので、これで問題が解決するとは思えません。
いつ実行するかを決めるのではなく、マシンに強制的にデータをディスクにダンプさせる方法や、ファイルを更新するより良い方法はありますか?
アップデート:
@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);
}
ベストアンサー1
FileStream.Flush
を使用してデータを強制的にディスクに書き込むことができます。一時ファイルに書き込み、 を使用してFile.Replace
ターゲット ファイルをアトミックに置き換えます。
これは確実に動作すると思います。ファイル システムの保証は弱いです。これらの保証はほとんど文書化されておらず、複雑です。
あるいは、使用可能な場合は Transactional NTFS を使用することもできます。これは .NET で使用できます。
FileOptions.WriteThrough
置き換えることはできますFlush
が、データのサイズが 1 つのクラスターを超える可能性がある場合は、依然として一時ファイルが必要です。