Потеря питания после того, как StreamWriter.Close() создает пустой файл, почему?
-
07-07-2019 - |
Вопрос
Хорошо, давайте объясним;Я разрабатываю систему, в которой может произойти сбой питания в любой момент времени, и я тестирую один момент сразу после того, как я записал файл с помощью StreamWriter.Код ниже:
// Write the updated file back out to the Shell directory.
using (StreamWriter shellConfigWriter =
new StreamWriter(@"D:\xxx\Shell\Config\Game.cfg.bak"))
{
for (int i = 0; i < configContents.Count; i++)
{
shellConfigWriter.WriteLine(configContents[i]);
}
shellConfigWriter.Close();
}
FileInfo gameCfgBackup = new FileInfo(@"D:\xxx\Shell\Config\Game.cfg.bak");
gameCfgBackup.CopyTo(@"D:\xxx\Shell\Config\Game.cfg", true);
Записывает содержимое shellConfigWriter
(а List
строк) в файл, используемый в качестве временного хранилища, затем он копируется поверх оригинала.Теперь, после завершения выполнения этого кода, питание пропадает, при повторном запуске файла файл Game.cfg
существует и имеет правильный размер, но совершенно пуст.Сначала я думал, что это связано с включенным Write-Caching на жестком диске, но даже при его выключении это все равно происходит (хотя и реже).
Любые идеи будут очень приветствоваться!
Обновлять: Итак, после удаления .Close()
заявления и звонки .Flush()
после каждой операции записи файлы по-прежнему остаются пустыми.Я мог бы пойти еще дальше и сначала создать резервную копию исходного файла, прежде чем создавать новый, и тогда у меня будет достаточно резервных копий для проверки целостности, но я не думаю, что это поможет решить основную проблему ( что когда я говорю ему записать, сбросить и закрыть файл...Это не так!).
Решение
Не позволяйте ОС буферизовать выходные данные с помощью параметра FileOptions
конструктора объекта FileStream
:
using (Stream fs = new FileStream(@"D:\xxx\Shell\Config\Game.cfg.bak", FileMode.Create, FileAccess.Write, FileShare.None, 0x1000, FileOptions.WriteThrough))
using (StreamWriter shellConfigWriter = new StreamWriter(fs))
{
for (int i = 0; i < configContents.Count; i++)
{
shellConfigWriter.WriteLine(configContents[i]);
}
shellConfigWriter.Flush();
shellConfigWriter.BaseStream.Flush();
}
Другие советы
Во-первых, вам не обязательно звонить shellConfigWriter.Close()
там.Тем using
заявление позаботится об этом.Вместо этого, чтобы защититься от сбоя питания, вы можете позвонить shellConfigWriter.Flush()
.
Обновлять
Еще кое-что, что вы, возможно, захотите принять во внимание, заключается в том, что если сбой в подаче электроэнергии действительно может произойти в любой время это может произойти в середине записи, так что в файл попадут только некоторые байты.На самом деле нет никакого способа остановить это.
Для защиты от таких сценариев обычно используют файлы флагов состояния/условия.Вы используете существование или отсутствие в файловой системе нулевого файла с определенным именем, чтобы сообщить вашей программе, откуда ей следует продолжить работу при возобновлении работы.Тогда вы не будете создавать и уничтожать файлы, вызывающие определенное состояние, пока не конечно вы достигли этого состояния и завершили предыдущее.
Обратной стороной здесь является то, что это может означать, что время от времени приходится выбрасывать много работы.Но преимущество в том, что это означает, что функциональная часть вашего кода выглядит нормально:для того, чтобы сделать систему достаточно надежной, нужно проделать очень немного дополнительной работы.
Вы хотите установить AutoFlush = true;