«Надежный» файл для работы с .NET - PullRequest
0 голосов
/ 29 января 2009

Приложению требуется счетчик, значение которого нужно сохранить в текстовом файле. Иногда это происходит на очень короткие промежутки времени.

Этот тестовый код очень часто переписывает текстовый файл (например, каждые 100 миллисекунд):

int counter = 0;

while (true)
{
    WriteToFile(counter);
    counter++;
    Thread.Sleep(100);
}

private void WriteToFile(int counter)
{
    byte[] buffer = Encoding.ASCII.GetBytes(counter.ToString());
    using (FileStream createFile = new FileStream("Counter.txt", FileMode.Create))
    {
        createFile.Write(buffer, 0, buffer.Length);
        createFile.Close();
    }
}

Он работает в основном нормально, за исключением одного из наших «надежных тестов» - прекращение подачи электроэнергии на компьютер во время работы приложения.

Плохим сюрпризом было - в файле не было никакого текста (должно быть число), а только один или два пробела.

Пропущенное последнее значение, которое он пытается записать, понятно, но сделать файл непоследовательным - это очень плохо ...

Попытка использования:

createFile.Flush();
createFile.Close();
createFile.Dispose();

GC.Collect();
GC.WaitForPendingFinalizers();

не помогает.

Ответы [ 5 ]

6 голосов
/ 29 января 2009

Различные варианты приходят на ум: -

1) сохранить временную копию текущего файла, например, переименуйте старый файл, напишите новый файл, удалите старый файл (или напишите новый файл с временным именем, удалите старый файл, переименуйте новый файл), но, возможно, вы пишете файл слишком часто для такого рода вещей

2) записать несколько файлов с периодической очисткой, например, 0001.txt, 0002.txt, 0003.txt, ... и т. Д. И каждые 10 секунд удаляйте все файлы, кроме последнего.

3) Пишите в один и тот же файл несколько раз, возможно, создавая новый файл периодически, скажем, каждые 10 минут, снова с периодической очисткой.

4) Используйте стороннюю платформу журналирования, такую ​​как log4net, которая, вероятно, защищает от таких вещей, как перебои в подаче электроэнергии, и может регистрировать данные для различных целей, например, файл, база данных, IRC

2 голосов
/ 29 января 2009

Я думаю, что проще всего было бы использовать транзакционную файловую систему в Windows Vista / 2008:

http://www.michaelckennedy.net/blog/2007/12/07/SystemTransactionsAndWindowsVistaNTFSUpdated.aspx

http://msdn2.microsoft.com/magazine/2fc4ae05-f7b8-49d2-8630-f24bc9dfc2e6

То есть, если вы один из немногих, кто им управляет ...

1 голос
/ 29 января 2009

отключить «оптимизировать для производительности» в разделе файловой системы. Вы пишете файл, но Windows не пытается записать данные на физический диск, вместо этого она хранит их в памяти, намереваясь в конечном итоге записать их, т.е. когда есть время простоя.

Очевидно, вы не даете ему простоев:)

Итак, отключите этот флаг и заставьте Windows каждый раз записывать содержимое на диск. Это похоже на тот же флаг, который вы используете для флэш-накопителей, чтобы убедиться, что данные действительно записаны перед тем, как вы извлечете устройство из USB-порта. Вы можете заставить Windows записывать данные, используя API-вызов FlushFileBuffers .

Или вы можете использовать параметр FILE_FLAG_WRITE_THROUGH .

Очевидно, убедитесь, что ваше оборудование также записывает данные - если у вас есть карта RAID, она будет кэшировать запись данных сама, вам потребуется батарея для ее резервного копирования, чтобы обеспечить физическую запись данных на диск.

Я думаю, что вы не будете довольны исполнением. Лучше всего тогда записывать на диск, используя рейдовую карту с батарейным питанием.

1 голос
/ 29 января 2009

В вашем файле отсутствуют значения, так как прерывание питания закрыло его до того, как Windows смогла записать его на диск. Вы можете вызывать Flush, Close, Dispose и т. Д. Все, что хотите, но они не могут работать, если сбой питания прерывает вызов. В лучшем случае они уменьшат окно уязвимости.

Ответ Адама Ральфа, я думаю, является наиболее подходящим для работы в любой системе. Я проголосовал за него, но хотел добавить свои 0,02 доллара в связи с ошибкой ваших системных вызовов для решения этой проблемы.

0 голосов
/ 29 января 2009

Спасибо всем за ответы.

Слушая их решение на данный момент стало:

использование (FileStream createFile = new FileStream ("Counter.txt", FileMode.Create, FileAccess.Write, FileShare.None, 8, FileOptions.WriteThrough)) {}

Все параметры включены из-за последнего - FileOptions.WriteThrough. Его цель - избежать использования любого вида денег, буферов, памяти и т. Д.

Поскольку это решение не дает 100% гарантий (из-за аппаратных причин :)), резервная копия сохраняется. Фактически каждая запись выполняется дважды - в оригинале и в файле резервной копии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...